为什么包含<utility>会破坏GCC中的结构化绑定?

rob*_*.14 21 c++ gcc c++17 structured-bindings gcc7

考虑:

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}
Run Code Online (Sandbox Code Playgroud)

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

#include <utility>

struct Point { int x, y; };

int main()
{
    const auto [x, y] = Point{};
}
Run Code Online (Sandbox Code Playgroud)

给出错误:

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{};
                ^~~~~~
Run Code Online (Sandbox Code Playgroud)

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

Bar*_*rry 19

这是编译器错误78939.虽然它比这复杂一点 - 核心语言和库之间存在一些相互矛盾的问题(GB 20,LWG 2770LWG 2446),这导致了gcc/libstdc ++在这里展示的那种行为.这当然是预期的是有或没有工作代码#include <utility>,它只是一个具有得到的标准写法有正确的事情.


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

否则,所有E非静态数据成员都应是公共直接成员E或同一明确公共基类的成员E,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();
Run Code Online (Sandbox Code Playgroud)

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

  • 这是由CWG和LWG不同步导致的标准错误,不是一个编译器错误(它实现了对字母的规范).最初结构化绑定的第2步检查了`tuple_size <T> :: value`,但`tuple_size <cv T>`不是SFINAE友好的.因此,如果`tuple_size <T> :: value`不起作用,LWG将`tuple_size <cv T>`改为空.然后GB 20说检查`tuple_size <T> :: value`太用户敌对了,所以现在核心只是检查`tuple_size`是否完整,如果是,则提交到类似元组的机制.当然,这会对图书馆修复`tuple_size <cv T>造成严重破坏. (10认同)

MSa*_*ers 12

结构化绑定背后的核心思想是std::tuple_size<T>定义从解包中获得的组件数量T,并且T::get<N>应该访问第N个元素.这并不奇怪,这std::tuple_size<T>是基础模板的专业化<utility>.

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

编译器绊倒自己这里,并试图使用通用的规则,当它看到的非特std::tuple_size<utility>.