【问题标题】:Initializer list and structured bindings deduction ambiguity in C++17C++17 中的初始化列表和结构化绑定推导歧义
【发布时间】:2017-06-21 03:02:15
【问题描述】:

我一直避免像下面这样的初始化

const auto& x = a, y = b;
const int* const x = ptr_1, *const y = ptr_2;  // wot

因为引用和指针限定符不适用于初始化。诚然它是初学者首先学习的东西之一,与之相关的模棱两可让我觉得下面的内容更清晰,读者需要更少的思考

const auto& x = a;
const auto& y = b;

对于 C++17 和结构化绑定,我很高兴并且看到了很多潜力。 C++17 禁止 C++14 和 C++11 未能修复的内容,auto x {1}int 而不是 std::initializer_list<int>。但是为什么下面的代码不起作用呢?

const auto& [x, y] {a, b};
const auto& [x, y] = {a, b};

后者符合自动推导和初始化列表的新规则,右侧的表达式被视为初始化列表。但是对于以前的编译失败并出现以下错误

initializer for variable '[a, b]' with type 'const auto &' contains multiple expressions

有什么方法可以使用结构化绑定语法同时声明 x 和 y,而不必求助于元组、对等?另外为什么上面的代码示例中的前者是格式错误的代码?该语法是否有歧义?

【问题讨论】:

    标签: c++ c++17 structured-bindings


    【解决方案1】:

    可以说,结构化绑定用于“拆包”事物。它的设计目的不是为了组合普通声明。 const auto& 既不适用于 a 也不适用于 b,尽管外观如此。

    您的特定尝试违反了[dcl.dcl]/8

    带有 identifier-listsimple-declaration 称为 结构化绑定声明 ([dcl.struct.bind])。 [...] initializer 的格式应为“= <em>assignment-expression</em> ”、“{ <em>assignment-expression</em> }”或 形式为“( <em>assignment-expression</em> )”,其中 assignment-expression 是数组或非联合类类型。


    int a = 1, b = 2;
    const auto bitand <:x, y:> = std::tie(a, b);
    

    这个结构化的绑定声明(非常)大致相当于

    const auto bitand __e = std::tie(a, b); // hidden variable
    auto and x = std::get<0>(__e);
    auto and y = std::get<1>(__e);
    

    (真实使用tuple_element,而不是auto。)

    注意事项:

    • const auto bitand 适用于隐藏变量,并且仅适用于隐藏变量。 xy 始终是引用,即使您只写 auto;它们的引用对象是否为 const 取决于初始化器类型的 const 传播属性。
    • 从纯右值初始化程序具体化的临时对象将通过引用绑定延长其生命周期。
    • 在本例中,xy 都是“引用 int”类型;写x = 1;有效。
    • decltype 措辞中对结构化绑定进行了特殊处理。

    如果我们谈论的是解包一个带有两个“对 int 的引用”成员的结构等,这些语义就不足为奇了; const 在这些事情上实际上并不影响所指对象的 constness。 OTOH,如果您想将结构化绑定声明用于它们不适合做的事情,那么您会大吃一惊。

    【讨论】:

    • 为什么xy的类型是int&amp;而不是const int&amp;__e 的类型是 const std::tuple&lt;int&amp;, int&amp;&gt;&amp;,所以调用 std::get&lt;&gt; 应该返回一个 const 引用类型吧?
    • 根据您的示例,xy 将始终是引用类型,但在此示例中,它们不是 wandbox.org/permlink/Dligcds69j04EwY0
    • @Curious decltype 专门处理结构绑定。
    • @Curious 当然,如果你有两个tie,你会得到一个tuple&lt;const int &amp;, const int&amp;&gt;。 /叹息
    • 您是否故意将&amp;&amp;&amp; 替换为多图?
    【解决方案2】:

    只是不支持这种语法。您只能解压缩已重载 std::get 的聚合类和对象:https://skebanga.github.io/structured-bindings/

    不幸的是,您不能真正使用酷推导指南,因为您想要引用 a 而不是元组成员。因此你必须写出模板参数列表。

    #include <tuple>
    
    int main()
    {
      int a = 1;
      int b = 2;
      const auto& [x, y] = std::tuple<int&,int&>{a, b};
    }
    

    你也不可能像我一样愚蠢并正确阅读文档。

    #include <tuple>
    
    int main()
    {
      int a = 1;
      int b = 2;
      const auto& [x, y] = std::forward_as_tuple(a, b);
    }
    

    const auto&amp; [x, y] = std::tie(a, b); 也可以。

    【讨论】:

    • const auto&amp; 具有极大的误导性。尝试写x = 1;
    • @T.C.有趣的。知道为什么 const 在这里被忽略了吗?
    • @HenriMenke 这是因为如果您创建 std::tuple&lt;int&amp;, int&amp;&gt; 然后调用 std::get&lt;&gt; ,则 const 不会传播到指向引用的值。当应用于不传播到实际值而仅传播到指针本身的指针时,这与 const 相同。
    • You can only unpack struct and tuple 完全错误。您可以(也)解压缩任何有std::get() 专业化的东西。原始数组,具有奇异语义的用户定义类型,无关紧要。
    • @underscore_d 这是skebanga.github.io/structured-bindings 的建议。我没有阅读整个提案。我编辑了我的答案以反映您的改进。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    相关资源
    最近更新 更多