空嵌套元组错误 [英] Empty nested tuples error

查看:362
本文介绍了空嵌套元组错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  #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
}

为什么Line 1会发生编译错误,精细? (在Gcc& Clang中测试)



是否有可能的解决方法?



p>

  / usr / include / c ++ / 4.6 / tuple:150:50:error:从派生类'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& :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:注意:在成员函数'std :: _ Tuple_impl< 0,std :: tuple< ;,
std :: tuple< std :: tuple<> > > :: _ M_head'request here
_Base(std :: forward< _Head>(__ in._M_head())){}
^
/usr/include/c++/4.6/tuple :334:9:note:在实例化成员函数'std :: _Tuple_impl< 0,std :: tuple<>,
std :: tuple< std :: tuple< > > :: _ Tuple_impl'request here
:_Inherited(static_cast <_Inherited&&>(__ in)){}
^
gcc_bug.cpp:5:10:note:in instantiation的成员函数
'std :: tuple< std :: tuple<>,std :: tuple< std :: tuple< > > :: tuple'requested here
auto bt = std :: make_tuple(std :: tuple<>(),std :: tuple< std :: tuple<>
^
生成了1个错误。


解决方案

看起来你在libstdc ++中发现了一个错误! (这段代码与libc ++一起工作)。减少的测试用例:

  #include< tuple> 

int main(){
auto b = std :: tuple< std :: tuple< std :: tuple<>>
}






如何 std :: tuple 在libstdc ++中实现。元组实现使用具有多重继承的递归。您可以将 X 继承自 tuple< X,Y,Z& tuple< Y,Z> 。这意味着 tuple< tuple<>> 将继承 tuple<> tuple<> ,这将导致不明确的基本错误。当然真正的问题不是这样,因为 tuple< tuple<>> 不会产生任何错误。



导致错误的真正实现是这样的:

 模板< size_t _Idx,typename _Head> 
struct _Head_base:public _Head
{};

template< size_t _Idx,typename ... _Elements>
struct _Tuple_impl;

template< size_t _Idx>
struct _Tuple_impl< _Idx> {};

template< size_t _Idx,typename _Head,typename ... _Tail>
struct _Tuple_impl< _Idx,_Head,_Tail ...>
:public _Tuple_impl< _Idx + 1,_Tail ...>,
private _Head_base< _Idx,_Head>
{
typedef _Tuple_impl< _Idx + 1,_Tail ...> _遗传;
constexpr _Tuple_impl()= default;
constexpr _Tuple_impl(_Tuple_impl&& __in):_Inherited(std :: move(__ in)){}
};

template< typename ... _Elements>
struct tuple:public _Tuple_impl< 0,_Elements ...> {};

当我们实例化 tuple< tuple< tuple<>> ,我们得到这个继承层次结构:



中的< code> tuple< tuple<>>>< / code>的继承图我们看到 _Tuple_impl< 1> 在两个不同的路径中可达。这还不是问题,问题是在移动构造函数,他调用 _Tuple_impl< 1> 的移动转换构造函数。 _Tuple_impl< 1> 你想要吗?编译器不知道,所以它选择放弃。



(在你的case是因为 _Head_base< 0,tuple< < tuple<>,< tuple<>> ,但原理是)






为什么libc ++不会有同样的问题?有两个主要原因:


  1. tuple< T ...> libc ++使用组合而不是继承来引用 __ tuple_impl< ...>

  2. __ tuple_leaf< tuple< tuple<>> 不会踢入,即 __ tuple_leaf< tuple< tuple& 不会继承 tuple< tuple<>>



>



正如我们可以看到的,如果 tuple& / code>使用继承而不是组合,OP的 tuple< tuple<>,tuple< tuple<>> $ c> __ tuple_leaf< 0,tuple<> 两次,这可能是一个问题。


#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.

解决方案

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

#include <tuple>

int main(){
    auto b = std::tuple<std::tuple<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<X, Y, Z> as inheriting from both X and tuple<Y, Z>. This means tuple<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<tuple<>> doesn't produce any error.

The real implementation that caused the error is like this:

template<size_t _Idx, typename _Head>
struct _Head_base : public _Head
{};

template<size_t _Idx, typename... _Elements>
struct _Tuple_impl;

template<size_t _Idx>
struct _Tuple_impl<_Idx> {};

template<size_t _Idx, typename _Head, typename... _Tail>
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<typename... _Elements>
struct tuple : public _Tuple_impl<0, _Elements...> {};

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


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

  1. tuple<T...> in libc++ use composition instead of inheritance to refer to __tuple_impl<...>.
  2. As a result, the empty base class optimization in __tuple_leaf<tuple<tuple<>>> does not kick in, i.e. __tuple_leaf<tuple<tuple<>>> won't inherit from tuple<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.)

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

这篇关于空嵌套元组错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆