空嵌套元组错误

时间:2012-06-12 14:45:04

标签: c++ c++11 nested tuples

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

为什么第1行产生编译错误而第2行编译正常? (在Gcc&amp; Clang中测试)

是否有可行的解决方法?

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.

2 个答案:

答案 0 :(得分:12)

看起来您在libstdc ++中发现了一个错误! (此代码与libc ++一起使用)。减少测试用例:

#include <tuple>

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

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

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

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...> {};

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

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

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

(在你的情况下,由于_Head_base<0, tuple<>>因为你正在实例化tuple<tuple<>, tuple<tuple<>>>,但原则是相同的。)


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

    libc ++中的
  1. tuple<T...>使用组合而不是继承来引用__tuple_impl<...>
  2. 因此,__tuple_leaf<tuple<tuple<>>>中的空基类优化没有启动,即__tuple_leaf<tuple<tuple<>>>不会继承tuple<tuple<>>
  3. 因此,模糊的基类问题不会发生。
  4. (并且每个基地都是独特的,如@mitchnull所述,但这不是主要区别。)
  5. inheritance diagram of <code>tuple<tuple<tuple<>>></code> in libc++

    正如我们上面所看到的,如果tuple<...>使用继承而不是合成,OP的tuple<tuple<>, tuple<tuple<>>>仍将继承__tuple_leaf<0, tuple<>>两次,这可能是一个问题。

答案 1 :(得分:0)

顺便说一句,对于那些必须使用gcc的人,让我给你一个快速且修复(针对4.8.0,已提交错误报告):

该解决方案是元组实现中__empty_not_final的一个小修改,以防止元组的空基优化&lt;&gt;输入:

template<typename _Tp>
    using __empty_not_final
      = typename conditional<__is_final(_Tp)||is_same<_Tp,tuple<>>::value,
false_type, is_empty<_Tp>>::type;

而不是

template<typename _Tp>
    using __empty_not_final
      = typename conditional<__is_final(_Tp), false_type, is_empty<_Tp>>::type;

(注意,这只是元组&lt;&gt;类型的特殊解决方案,它不能解决KennyTM描述的实际问题,即struct A{}; auto d = std::tuple<std::tuple<std::tuple<A, A>, A>, A>{};仍然无法编译)