结构化绑定和转发引用是否组合良好?

时间:2018-04-09 08:31:41

标签: c++ language-lawyer c++17 forwarding-reference structured-bindings

我知道我能做到

auto&& bla = something();

并且取决于const的返回值的something,我为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;

我仍然想确切知道这种行为的确切位置。

注意:使用&#34;转发参考&#34;这意味着这种行为可能会延长它,但我没有一个好名字来提供const的{​​{1}}扣除部分(或模板 - auto&&)。

1 个答案:

答案 0 :(得分:8)

是。结构化绑定和转发参考混合良好

一般情况下,任何地方都可以使用auto,您可以使用auto&&获取不同的含义。具体来说,对于结构化绑定,这来自[dcl.struct.bind]

  

否则,e被定义为 - 如果

     

attribute-specifier-seq opt decl-specifier-seq ref-qualifier opt < / sub> e 初始值设定项 ;

     

其中声明永远不会被解释为函数声明,并且除了 declarator-id 之外的声明部分取自相应的结构化绑定声明。

[dcl.dcl]中的这些部分还有其他限制:

  

带有标识符列表简单声明称为结构化绑定声明([dcl.struct.bind])。 decl-specifier-seq 只应包含类型说明符 auto cv - 限定符。 初始值设定项的格式为“= assignment-expression ”,格式为“{ assignment-expression }“,或者格式为”( assignment-expression )“,其中 assignment-expression 是数组或非联盟类型。

把它放在一起,我们可以打破你的榜样:

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

autoU取代,&&继承。这是我们的转发参考。如果扣除失败(只有当something()void时),我们的声明就会形成错误。如果成功,我们抓住推导出的U并将我们的声明视为:

U&& e = something();

根据e的值类别和类型,使something()成为左值或左值引用,而const为非限定值。

其余结构化绑定规则遵循[dcl.struct.bind],基于e的基础类型,something()是否为左值,以及是否{{ 1}}是左值参考。

有一点需要注意。对于结构化绑定,e始终是referenced type,而不是您可能期望的类型。例如:

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)(引用的类型),而不是intint&行为的有意义的方式)。要记住的事情。

我可以想到两个地方不是这样的。

trailing-return-type 声明中,您必须只使用a。你不能写,例如:

auto

我能想到的另一个地方可能不是这种情况,这是概念TS的一部分,您可以在更多地方使用auto&& foo() -> decltype(...); 来推断/约束类型。在那里,当您推断的类型不是参考类型时使用转发引用我认为是不正确的:

auto