21

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?

ks1322
  • 33,961
  • 14
  • 109
  • 164
robson3.14
  • 3,028
  • 2
  • 20
  • 19

2 Answers2

19

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
Barry
  • 286,269
  • 29
  • 621
  • 977
  • @MSalters Man, gcc bugzilla search sucks. I looked for a few minutes and couldn't find anything... – Barry May 10 '17 at 14:38
  • 10
    This is a standard bug caused by CWG and LWG not being in sync, not quite a compiler bug (it implements the specification to the letter). Originally step 2 of structured bindings checked `tuple_size::value`, but `tuple_size` wasn't SFINAE-friendly. So LWG changed `tuple_size` to be empty if `tuple_size::value` doesn't work. Then GB 20 says that checking `tuple_size::value` is too user-hostile, so now core just checks if `tuple_size` is complete, and if so commits to the tuple-like mechanism. Of course that then wreaked havoc with library's fix to `tuple_size`. – T.C. May 10 '17 at 18:22
  • @T.C. Do you know, is there a plan to fix that? Is there a chance to get it fixed in C++17 or would we have a "hotfix" just after it is published? – Ilya Popov May 12 '17 at 11:14
  • @T.C. Thanks for the edit. Odd that the old link still works, confused me for a while. Just an artifact? Wording itself looks the same to me. – Barry May 12 '17 at 19:54
  • @Barry Maybe they just copied the new files over without cleaning up the old ones when updating. IDK. – T.C. May 12 '17 at 20:40
12

The core idea behind structured bindings is that std::tuple_size<T> defines how many components you get from unpacking T, and T::get<N> should access the N'th element. Not surprising, this std::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>.

MSalters
  • 173,980
  • 10
  • 155
  • 350