结构化绑定和转发引用能很好地混合吗?

我知道我可以做到

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;
}

Live demo,如果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;

我仍然想知道此行为的确切指定位置。

注意:使用";Forwarding References&Quot;表示此行为可能会夸大它,但我没有一个好的名称来给auto&&const扣减部分(或该问题的模板-T&&)。


解决方案

是。结构化绑定和转发引用很好地混合在一起?

一般来说,任何地方?都可以使用auto,可以使用auto&&获取不同的含义。特别是对于结构化绑定,它来自[dcl.struct.bind]:

否则,e

定义为-if

attribute-specifier-seqoptDECL-说明符-序号引用限定符选项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](具体地说是here)。在那里,我们确实对初始值设定项:

进行了演绎
template <typename U> void f(U&& );
f(something());

其中auto替换为U,而&&继续。这是我们的发货证明。如果演绎失败(只有在something()void时才会失败),我们的声明就是格式错误的。如果成功,我们将获取推导出的U,并将我们的声明视为:

U&& e = something();

根据something()的值类别和类型,使e成为左值或右值引用,即常量不符合条件。

结构化绑定规则的睡觉跟随在[dcl.struct.bind]中,根据e的底层类型,something()是否为左值,e是否为左值引用。


?有一个警告。对于结构化绑定,decltype(e)始终是referenced type,而不是您预期的类型。例如:

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(...);

我能想到的唯一不是这种情况的地方是Concepts 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&&

相关文章