c++gccc++17structured-bindingsgcc7

Why does including <utility> break structured bindings in GCC?


Consider:

struct Point { int x, y; };

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

This code compiles fine with gcc 7.1 in C++17 mode, however this one:

#include <utility>

struct Point { int x, y; };

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

gives an error:

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{};
                ^~~~~~

What's going on here? A compiler bug, or is this how structured bindings are supposed to work?


Solution

  • This is compiler bug 78939. Although it's a bit more complicated than that - there were a few issues between the core language and the library that were mutually contradictory (GB 20, LWG 2770, and LWG 2446), which lead to the kind of behavior that gcc/libstdc++ exhibit here. It is certainly intended that the code work with or without #include <utility>, it's just a matter of the standard wording having gotten there properly.


    Yes, classes with all public non-anonymous union members should be usable in structured bindings declarations per [dcl.struct.bind]/4:

    Otherwise, all of E's non-static data members shall be public direct members of E or of the same unambiguous public base class of E, E shall not have an anonymous union member, and the number of elements in the identifier-list shall be equal to the number of non-static data members of E. Designating the non-static data members of E as m0, m1, m2, ... (in declaration order), each vi is the name of an lvalue that refers to the member mi of e and whose type is cv Ti, where Ti is the declared type of that member; the referenced type is cv Ti. The lvalue is a bit-field if that member is a bit-field. [ Example:

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

    This is completely unrelated to the inclusion of <utility>, nothing in this code depends on any library functionality - the members are grabbed directly, and not via the get/tuple_size mechanism.