Empty nested tuples error

前端 未结 2 1328
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-12 21:46
#include 
#include 
int main(){

auto bt=std::make_tuple(std::tuple<>(),std::tuple>()); //Line 1
auto bt2         


        
2条回答
  •  梦毁少年i
    2021-01-12 22:09

    Looks like you found a bug in libstdc++! (This code works in clang with libc++). A reduced test case:

    #include 
    
    int main(){
        auto b = std::tuple>>{};
    }
    

    The problem is due to how std::tuple is implemented in libstdc++. The tuple implementation uses "recursion" with multiple-inheritance. You can think of tuple as inheriting from both X and tuple. This means tuple> will inherit from both tuple<> and tuple<> and that will cause an ambiguous base error. Of course the real problem isn't like this, because tuple> doesn't produce any error.

    The real implementation that caused the error is like this:

    template
    struct _Head_base : public _Head
    {};
    
    template
    struct _Tuple_impl;
    
    template
    struct _Tuple_impl<_Idx> {};
    
    template
    struct _Tuple_impl<_Idx, _Head, _Tail...>
        : public _Tuple_impl<_Idx + 1, _Tail...>,
          private _Head_base<_Idx, _Head>
    {
        typedef _Tuple_impl<_Idx + 1, _Tail...> _Inherited;
        constexpr _Tuple_impl() = default;
        constexpr _Tuple_impl(_Tuple_impl&& __in) : _Inherited(std::move(__in)) {}
    };
    
    template
    struct tuple : public _Tuple_impl<0, _Elements...> {};
    

    When we instantiate tuple>>, we get this inheritance hierarchy:

    inheritance diagram of <code>tuple<tuple<tuple<>>></code> in libstdc++

    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 instantiating tuple, tuple>> instead, but the principle is the same.)


    Why libc++ does not have the same problem? There are two main reasons:

    1. tuple in libc++ use composition instead of inheritance to refer to __tuple_impl<...>.
    2. As a result, the empty base class optimization in __tuple_leaf>> does not kick in, i.e. __tuple_leaf>> won't inherit from tuple>
    3. Therefore, the ambiguous base class problem won't happen.
    4. (and each base is unique as mentioned by @mitchnull, but that is not a main difference here.)

    inheritance diagram of <code>tuple<tuple<tuple<>>></code> in libc++

    As we can see above, if tuple<...> uses inheritance instead of composition, OP's tuple, tuple>> will still inherit from __tuple_leaf<0, tuple<>> twice, which might be a problem.

提交回复
热议问题