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?
The core idea behind structured bindings is that
std::tuple_size<T>
defines how many components you get from unpackingT
, andT::get<N>
should access the N'th element. Not surprising, thisstd::tuple_size<T>
is a specialization from the base template in<utility>
.Now in this case,
Point
doesn't have such support for structured bindings, but it is a special case (all public non-static members) for which C++17 states that no special unpacking support is needed. This is an exception to the rule above.The compiler is tripping over itself here, and trying to use the generic rule when it sees the unspecialized
std::tuple_size
from<utility>
.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:
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 theget
/tuple_size
mechanism.