17
struct X { int a, b; };

int main()
{
    auto p = std::pair{ 1, 2 };
    const auto&[r1, r2] = p; // ok

    X x{ 1, 2 };
    const auto&[r3, r4] = x; // error
}

clang 7.0 (on Windows) 's error message:

error : cannot decompose this type; 'std::tuple_size<const X>::value' is not a valid 
           integral constant expression

Why does structured binding not work as expected on struct?

xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • 3
    [Cannot reproduce](https://coliru.stacked-crooked.com/a/5aec398420acb24f), not even with [clang](https://coliru.stacked-crooked.com/a/6fcaa0307a6b0a0e). – nwp Dec 11 '18 at 10:09
  • 1
    [Works on Clang 5.0](https://coliru.stacked-crooked.com/a/88396b3c8f9763eb). This is a bug. – StoryTeller - Unslander Monica Dec 11 '18 at 10:11
  • 2
    Possibly related: https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg14608.html – Daniel Langr Dec 11 '18 at 10:16
  • 1
    The same error with clang 6 on linux. It does not compile. – Ring Zero. Dec 11 '18 at 10:48
  • There are weird error with clang 7.0.0 depending on the plateform. On arch linux I find bug I cannot reproduce on compiler explorer. I suppose this related to the fact that clang 7.0 support for c++17 is much more experimental than gcc c++17 supports. (There are many other terrible bugs like clang silently corrupting its internal state while not producing any error message when using template constexpr variable member of template class, Finding this bug cost me so much time that I don't use any more clang to compile c++17 code). – Oliv Dec 11 '18 at 12:13
  • 1
    (The thing is, the core language specifies that structured binding declaration uses `std::tuple_size::value` whenever `std::tuple_size` is defined, and the standard library defines `std::tuple_size` for all const type `T`.) – cpplearner Dec 11 '18 at 12:36
  • @cpplearner In [tuple.helper]/4 the *Otherwise, they shall have no member value.* is superseded by *All specializations of tuple_­size shall satisfy the Cpp17UnaryTypeTrait requirements ([meta.rqmts]) with a base characteristic of integral_­constant for some N.* So if their shall not have value member, their should not be any specialization. – Oliv Dec 11 '18 at 12:58
  • @Oliv It can have no member `value` but still be complete (e.g. `template <> struct tuple_size { };`) – Barry Dec 11 '18 at 13:03
  • @Barry Yes that is feasable, but in this case it does not fullfill the Cpp17UnaryTypeTrait requirement. So this is libc++ implementation bug. In libstdc++, the specialization is explicitly disabled in this case. – Oliv Dec 11 '18 at 13:04
  • @Oliv `tuple_size` only has to fulfill Cpp17UnaryTypeTrait if `tuple_size::value` exists. Otherwise, the only requirement is that `value` doesn't exist. – Barry Dec 11 '18 at 13:11
  • @Barry `tuple_size` when specialized for some T is a specialization of `tuple_size`. So this requirement applies *All specializations of tuple_­size shall satisfy the Cpp17UnaryTypeTrait *. Then the question is why is there *it shall have no value member*. An incomplete type does not have a value member either. So the only way to fullfill the standard requirement is not to disable specialization of `tuple_size is `tuple_size` is not specialized. – Oliv Dec 11 '18 at 13:17
  • @Barry And honestly, is it possible that the intent of the committee was to make the above code ill-formed? I suppose they just stood on the *All specializations of tuple_­size shall satisfy the Cpp17UnaryTypeTrait* – Oliv Dec 11 '18 at 13:19
  • @Oliv Oh. Good point (re: all specializations...). But there's no way that `const auto& [a, b] = x;` is _supposed_ to be ill-formed, that'd be nuts. – Barry Dec 11 '18 at 13:52
  • @Barry Since you are there, I may have a related question: https://stackoverflow.com/questions/53726135/shall-structured-binding-to-a-copy-of-a-const-c-array-be-const – Oliv Dec 11 '18 at 14:22

1 Answers1

12

This is a known bug. See https://bugs.llvm.org/show_bug.cgi?id=33236.

Basically, the problem is, the C++17 standard as written specifies that structured binding declaration treats T as a tuple-like type and uses std::tuple_size<T>::value whenever std::tuple_size<T> is defined; but it also specifies that the standard library defines std::tuple_size<T> for all const type T.

That means, when compiling const auto&[r3, r4] = x;, Clang looks for std::tuple_size<const X>, and finds the definition in the standard library (which is provided by MSVC). Since the definition of std::tuple_size<const X> is successfully found, Clang attempts to use the "tuple-like" binding protocol, and it sure enough fails: const X is nothing like a tuple!

Suggestion from MSVC STL maintainer (source):

Workaround: don’t use const on the struct.

cpplearner
  • 13,776
  • 2
  • 47
  • 72