我知道我能做到
auto&& bla = something();
并且根据something
返回值的const
性,我会得到一个不同的bla
类型。
这是否也适用于结构化绑定情况,例如
auto&& [bla, blabla] = something();
我会猜是这样(结构化绑定搭载在auto
初始化器上,它们的行为是这样的),但我找不到明确的是。
更新:初步测试似乎达到了我的预期(正确推导出const
):
#include <tuple>
using thing = std::tuple<char, int*, short&, const double, const float&>;
int main()
{
char c = 0;
int i = 1;
short s = 2;
double d = 3.;
float f = 4.f;
thing t{c, &i, s, d, f};
auto&& [cc, ii, ss, dd, ff] = t;
c = 10;
*ii = 11;
ss = 12;
dd = 13.;
ff = 14.f;
}
现场演示,如果auto&&
正在做它的工作,我会给出错误:
main.cpp: In function 'int main()':
main.cpp:20:10: error: assignment of read-only reference 'dd'
dd = 13.;
^~~
main.cpp:21:10: error: assignment of read-only reference 'ff'
ff = 14.f;
我仍然想知道这种行为的确切指定位置。
注意:使用"转发引用"来表示这种行为可能会扩展它,但我没有一个好名字来给出auto&&
的const
扣除部分(或模板 -T&&
)。
是的。结构化绑定和转发引用混合得很好†。
一般来说,任何地方‡都可以用auto
,可以用auto&&
来获取不同的含义。特别是对于结构化绑定,它来自 [dcl.struct.bind]:
否则,
e
定义为属性-说明符-seqoptdecl-specifier-seq ref-qualifieropt
e
初始值设定项;
其中声明永远不会被解释为函数声明,声明中除声明符 ID之外的部分取自相应的结构化绑定声明。
[dcl.dcl] 中对这些部分有进一步的限制:
带有标识符列表的简单声明称为结构化绑定声明([dcl.struct.bind])。decl-specifier-seq应仅包含类型说明符
auto
和cv限定符。初始值设定项应为"=
赋值表达式"的形式,{
赋值表达式}
"的形式,或">(
赋值表达式)
"的形式,其中赋值表达式为数组或非联合类类型。
综上所述,我们可以分解您的示例:
auto&& [bla, blabla] = something();
声明这个未命名变量:
auto && e = something();
~~~~ ~~ ~~~~~~~~~~~
decl-specifier-seq initializer
ref-qualifier
该行为派生自 [dcl.spec.auto](特别是此处)。在那里,我们确实对初始值设定项进行扣除:
template <typename U> void f(U&& );
f(something());
其中auto
被U
取代,&&
继续。这是我们的转发参考。如果演绎失败(只有在void
的情况下才能something()
),我们的声明格式不正确。如果成功了,我们抓住推导的U
,把我们的声明当作:
U&& e = something();
这使得e
成为左值或右值引用,根据something()
的值类别和类型,常量符合 not 条件。
其余的结构化绑定规则遵循 [dcl.struct.bind],基于e
的基础类型,无论something()
是否为左值,以及e
是否为左值引用。
†有一个警告。对于结构化绑定,decltype(e)
始终是引用的类型,而不是您可能期望的类型。例如:
template <typename F, typename Tuple>
void apply1(F&& f, Tuple&& tuple) {
auto&& [a] = std::forward<Tuple>(tuple);
std::forward<F>(f)(std::forward<decltype(a)>(a));
}
void foo(int&&);
std::tuple<int> t(42);
apply1(foo, t); // this works!
我传递我的tuple
是一个左值,您希望将其底层元素作为左值引用传递,但它们实际上被转发了。这是因为decltype(a)
只是int
(引用的类型),而不是int&
(a
行为的有意义的方式)。要记住的事情。
‡我能想到的地方有两个地方不是这种情况。
在尾随返回类型声明中,必须仅使用auto
。你不能写,例如:
auto&& foo() -> decltype(...);
我能想到的唯一可能不是这种情况的其他地方是概念 TS 的一部分,您可以在更多地方使用auto
来推断/约束类型。在那里,当您推断的类型不是引用类型时,使用转发引用将是格式不正确的,我认为:
std::vector<int> foo();
std::vector<auto> a = foo(); // ok, a is a vector<int>
std::vector<auto&&> b = foo(); // error, int doesn't match auto&&