#include <iostream>
#include <tuple>
int main(){
auto bt=std::make_tuple(std::tuple<>(),std::tuple<std::tuple<>>()); //Line 1
auto bt2=std::make_tuple(std::tuple<>(),std::tuple<>()); //Line 2
}
Why does Line 1 gives a compile error while Line 2 compiles fine? (tested in both Gcc&Clang)
Is there a possible workaround?
error message for clang
/usr/include/c++/4.6/tuple:150:50: error: ambiguous conversion from derived class 'std::_Tuple_impl<0, std::tuple<>,
std::tuple<std::tuple<> > >' to base class 'std::_Head_base<0, std::tuple<>, true>':
struct std::_Tuple_impl<0, class std::tuple<>, class std::tuple<class std::tuple<> > > -> _Tuple_impl<0UL + 1, class std::tuple<class std::tuple<> > > -> _Head_base<1UL, class std::tuple<class std::tuple<> >, std::is_empty<class tuple<class tuple<> > >::value> -> class std::tuple<class std::tuple<> > -> _Tuple_impl<0, class std::tuple<> > -> _Head_base<0UL, class std::tuple<>, std::is_empty<class tuple<> >::value>
struct std::_Tuple_impl<0, class std::tuple<>, class std::tuple<class std::tuple<> > > -> _Head_base<0UL, class std::tuple<>, std::is_empty<class tuple<> >::value>
_Head& _M_head() { return _Base::_M_head(); }
^~~~~
/usr/include/c++/4.6/tuple:173:33: note: in instantiation of member function 'std::_Tuple_impl<0, std::tuple<>,
std::tuple<std::tuple<> > >::_M_head' requested here
_Base(std::forward<_Head>(__in._M_head())) { }
^
/usr/include/c++/4.6/tuple:334:9: note: in instantiation of member function 'std::_Tuple_impl<0, std::tuple<>,
std::tuple<std::tuple<> > >::_Tuple_impl' requested here
: _Inherited(static_cast<_Inherited&&>(__in)) { }
^
gcc_bug.cpp:5:10: note: in instantiation of member function
'std::tuple<std::tuple<>, std::tuple<std::tuple<> > >::tuple' requested here
auto bt=std::make_tuple(std::tuple<>(),std::tuple<std::tuple<>>());
^
1 error generated.
By the way, for those who have to use gcc, let me give you a quick and dirty fix (for 4.8.0, already submitted a bug report) :
The solution is a small modification of __empty_not_final in the tuple implementation, to prevent empty base optimisation for tuple<> type :
instead of
(Note that, this is only an adhoc solution for tuple<> type, it does not solve the real problem described by KennyTM, i.e.
struct A{}; auto d = std::tuple<std::tuple<std::tuple<A, A>, A>, A>{};
still does not compile)Looks like you found a bug in libstdc++! (This code works in clang with libc++). A reduced test case:
The problem is due to how
std::tuple
is implemented in libstdc++. The tuple implementation uses "recursion" with multiple-inheritance. You can think oftuple<X, Y, Z>
as inheriting from bothX
andtuple<Y, Z>
. This meanstuple<tuple<>>
will inherit from bothtuple<>
andtuple<>
and that will cause an ambiguous base error. Of course the real problem isn't like this, becausetuple<tuple<>>
doesn't produce any error.The real implementation that caused the error is like this:
When we instantiate
tuple<tuple<tuple<>>>
, we get this inheritance hierarchy:We see that
_Tuple_impl<1>
is reachable in two different paths. This is not yet the problem, the problem is in the move constructor, who invokes the move-conversion constructor of_Tuple_impl<1>
. Which_Tuple_impl<1>
do you want? The compiler doesn't know, so it chooses the give up.(In your case it's because of
_Head_base<0, tuple<>>
as you are instantiatingtuple<tuple<>, tuple<tuple<>>>
instead, but the principle is the same.)Why libc++ does not have the same problem? There are two main reasons:
tuple<T...>
in libc++ use composition instead of inheritance to refer to__tuple_impl<...>
.__tuple_leaf<tuple<tuple<>>>
does not kick in, i.e.__tuple_leaf<tuple<tuple<>>>
won't inherit fromtuple<tuple<>>
As we can see above, if
tuple<...>
uses inheritance instead of composition, OP'stuple<tuple<>, tuple<tuple<>>>
will still inherit from__tuple_leaf<0, tuple<>>
twice, which might be a problem.