I started from splitting the instruction into two parts (with the old-style initialization to bypass any problems related to std::initailizer_list
, if any):
s_int_triple s( 0, 0, 0 );
auto [b1, b2, b3] = s;
The compiler error reads:
main.cpp:27:10: error: cannot decompose class type ‘std::_Tuple_impl<1, int, int>’: its base classes ‘std::_Head_base<2, int, false>’ and ‘std::_Head_base<1, int, false>’ have non-static data members
27 | auto [b1, b2, b3] = s;
| ^~~~~~~~~~~~
So I conclude that the inheritance of a constructor is not a problem, the problem is with the structured binding.
C++ is an awful language when one has to look at its standard template library, but after loading the program to a decent IDE I found that in the gcc 10,2 implementation of std::tuple
, this class inherits publicly from _Tuple_impl<0, _Elements...>
and privatly from _Head_base<_Idx, _Head>
(multiple inheritance, recursive in the number of parameters). Now it's time to look for non-static data members of these classes. The first class looks so messy that I failed. The second, however, has this line in its declaration:
_Head _M_head_impl;
Bingo! This is a non-static data member.
Now its time to find the appropriate rule of structured binding decomposition. We go to https://en.cppreference.com/w/cpp/language/structured_binding and there, straight to "Case 3":
Every non-static data member of E must be a direct member of E or the same base class of E, and must be well-formed in the context of the structured binding when named as e.name. E may not have an anonymous union member. The number of identifiers must equal the number of non-static data members.
So here we have the answer: your class that derives from std::tuple
inherits form several other classes, too, and some of them define nonstatic members.
Simple example of this phenomenon:
struct A
{
int x;
};
struct B: public A
{
int y;
};
int main()
{
B obj;
auto [a, b] = obj;
}
leads to
main2.cpp:14:8: error: cannot decompose class type ‘B’: both it and its base class ‘A’ have non-static data members
So now we come to a real puzzle: if std::tuple
inherits from different classes, and each defines a nonstatic member, then why can we use structured binding for tuples? We go back to the cppreference link and see that std::tuple
is an exception to Case 3 described above. Compilers have to treat standard tuples and similar classes in their own, special way. In other words, std::tuple
and tuple-like classes are handled by Case 2, but any other class or struct is handled by far more restrictive (and very general) Case 3.
So, to make your program compile, you have to make your class "tuple-like", as described in the source cited above. I have no idea if it is possible at all - I guess this would deserve asking a separate question. How to do it is described in the answer by @yakk-adam-nevraumont