【问题标题】:Empty nested tuples error空嵌套元组错误
【发布时间】:2026-01-05 23:30:01
【问题描述】:
#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 和 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.

【问题讨论】:

  • 这可能是 Gcc 或 Clang 中的错误吗?您的代码使用 Visual Studio 2010 编译。顺便说一下,您声明 bt 两次,这是有意的吗?
  • 另外,你能指出编译错误信息吗?
  • 抱歉,错误的第 1 行已被注释掉。我忘记改名字了,现在编辑了。
  • 更可能是 libstdc++ 中的错误。如果可以,请尝试使用 libc++。
  • @ecatmur:不完全是。您链接中的代码在 gcc 4.7 中使用 libstdc++ 编译得很好,但 OP 的代码没有。

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


【解决方案1】:

看起来您在 libstdc++ 中发现了一个错误! (此代码与 libc++ 一起工作)。简化的测试用例:

#include <tuple>

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

问题在于 std::tuple 在 libstdc++ 中的实现方式。元组实现使用具有多重继承的“递归”。你可以认为tuple&lt;X, Y, Z&gt; 继承自Xtuple&lt;Y, Z&gt;。这意味着tuple&lt;tuple&lt;&gt;&gt; 将从tuple&lt;&gt;tuple&lt;&gt; 继承,这将导致模棱两可的基本错误。当然真正的问题不是这样的,因为tuple&lt;tuple&lt;&gt;&gt; 不会产生任何错误。

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

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&lt;tuple&lt;tuple&lt;&gt;&gt;&gt; 时,我们得到这个继承层次结构:

我们看到_Tuple_impl&lt;1&gt; 可以通过两条不同的路径访问。这还不是问题,问题出在移动构造函数中,它调用了_Tuple_impl&lt;1&gt; 的移动转换构造函数。你想要哪个_Tuple_impl&lt;1&gt;?编译器不知道,所以选择了放弃。

(在您的情况下,这是因为 _Head_base&lt;0, tuple&lt;&gt;&gt; 而您正在实例化 tuple&lt;tuple&lt;&gt;, tuple&lt;tuple&lt;&gt;&gt;&gt;,但原理是一样的。)


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

  1. libc++ 中的tuple&lt;T...&gt; 使用组合而不是继承来引用__tuple_impl&lt;...&gt;
  2. 因此,__tuple_leaf&lt;tuple&lt;tuple&lt;&gt;&gt;&gt; 中的空基类优化不会启动,即__tuple_leaf&lt;tuple&lt;tuple&lt;&gt;&gt;&gt; 不会从tuple&lt;tuple&lt;&gt;&gt; 继承
  3. 因此,基类不明确的问题不会发生。
  4. (正如@mitchnull 所提到的,每个碱基都是唯一的,但这不是这里的主要区别。)

正如我们在上面看到的,如果tuple&lt;...&gt; 使用继承而不是组合,OP 的tuple&lt;tuple&lt;&gt;, tuple&lt;tuple&lt;&gt;&gt;&gt; 仍然会从__tuple_leaf&lt;0, tuple&lt;&gt;&gt; 继承两次,这可能是个问题。

【讨论】:

  • 我明白了,libc++ 使用非递归元组实现吗?
  • 其实libc++在实现上也使用了多重继承,但是是“扁平化”的,每个基都是唯一的。详情可以在mitchnull.blogspot.com/2012/06/…查看我的帖子。
  • @mitchnull:其实我已经详细检查了libc++的实现,关键不是“扁平”继承,而是tuple&lt;tuple&lt;&gt;&gt;不是一个空类。查看更新。
  • @KennyTM 所以在 libc++ 中 sizeof(tuple>>)>1 ?
【解决方案2】:

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

解决方法是对元组实现中的__empty_not_final进行小修改,防止对元组类型进行空基优化:

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;

(请注意,这只是 tuple 类型的临时解决方案,它并不能解决 KennyTM 描述的真正问题,即 struct A{}; auto d = std::tuple&lt;std::tuple&lt;std::tuple&lt;A, A&gt;, A&gt;, A&gt;{}; 仍然无法编译)

【讨论】: