【问题标题】:Why does including <utility> break structured bindings in GCC?为什么包含 <utility> 会破坏 GCC 中的结构化绑定?
【发布时间】:2017-05-10 13:56:41
【问题描述】:

考虑:

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}

这段代码在 C++17 模式下使用 gcc 7.1 编译得很好,但是这个:

#include <utility>

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}

给出一个错误:

bug.cpp: In function 'int main()':
bug.cpp:7:16: error: 'std::tuple_size<const Point>::value' is not an integral constant expression
     const auto [x, y] = Point{};
                ^~~~~~

这里发生了什么?一个编译器错误,或者这是结构化绑定应该如何工作的?

【问题讨论】:

  • 我刚刚测试过,clang 4.0.0 的行为方式相同。
  • 在最近的 Kona 会议 (2017-02-27 - 2017-03-04) 中,这方面似乎发生了变化。此功能还不成熟。

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


【解决方案1】:

这是编译器错误78939。虽然比这要复杂一些——核心语言和库之间存在一些相互矛盾的问题(GB 20LWG 2770LWG 2446),这导致了 gcc/ libstdc++ 在这里展示。当然有意代码在有或没有#include &lt;utility&gt; 的情况下都可以工作,这只是标准措辞正确到达那里的问题。


是的,具有所有公共非匿名联合成员的类应该可以在每个 [dcl.struct.bind]/4 的结构化绑定声明中使用:

否则,E 的所有非静态数据成员都应是EE 的同一明确公共基类的公共直接成员,E 不应有匿名联合成员,并且标识符列表中的元素数量应等于E 的非静态数据成员的数量。将E的非静态数据成员指定为m0、m1、m2、...(按声明顺序),每个vi是一个左值的名称,它引用了e的成员mi,其类型为cv Ti,其中 Ti 是该成员的声明类型;引用的类型是 cv Ti。如果该成员是位域,则左值是位域。 [ 示例:

struct S { int x1 : 2; volatile double y1; };
S f();
const auto [ x, y ] = f();

这与 &lt;utility&gt; 的包含完全无关,此代码中的任何内容都不依赖于任何库功能 - 成员是直接获取的,而不是通过 get/tuple_size 机制。

【讨论】:

  • @MSalters 伙计,gcc bugzilla 搜索很烂。我找了几分钟,找不到任何东西......
  • 这是由于 CWG 和 LWG 不同步导致的标准错误,而不是编译器错误(它严格执行规范)。最初检查了结构化绑定的第 2 步 tuple_size&lt;T&gt;::value,但 tuple_size&lt;cv T&gt; 对 SFINAE 不友好。因此,如果 tuple_size&lt;T&gt;::value 不起作用,LWG 将 tuple_size&lt;cv T&gt; 更改为空。然后 GB 20 说检查 tuple_size&lt;T&gt;::value 对用户太不利,所以现在核心只检查 tuple_size 是否完整,如果是则提交到类似元组的机制。当然,这对库对 tuple_size&lt;cv T&gt; 的修复造成了严重破坏。
  • @T.C.你知道吗,有解决这个问题的计划吗?有没有机会在 C++17 中修复它,或者我们会在它发布后有一个“修补程序”吗?
  • @T.C.感谢您的编辑。奇怪的是旧链接仍然有效,让我困惑了一段时间。只是一个神器?措辞本身在我看来是一样的。
  • @Barry 也许他们只是在更新时复制了新文件而没有清理旧文件。 IDK。
【解决方案2】:

结构化绑定背后的核心思想是std::tuple_size&lt;T&gt; 定义了从T 解包得到多少组件,T::get&lt;N&gt; 应该访问第 N 个元素。毫不奇怪,这个std::tuple_size&lt;T&gt;&lt;utility&gt; 中基本模板的特化。

现在在这种情况下,Point 没有对结构化绑定的这种支持,但它是一种特殊情况(所有公共非静态成员),C++17 声明不需要特殊的解包支持。这是上述规则的一个例外。

编译器在此处跳闸,并在看到来自&lt;utility&gt; 的未专门化的std::tuple_size 时尝试使用通用规则。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-05-27
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    相关资源
    最近更新 更多