首页 文章

为什么这会超过最大递归模板深度?

提问于
浏览
22

我一直在使用可变参数模板并注意到以下内容 .

这很好用:

auto t = std::make_tuple(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);

这将给出错误(gcc 4.8.2(编辑:Clang 3.4)默认最大深度为256):

auto t2 = std::make_tuple(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);

但是,直接创建元组将起作用:

std::tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> t3(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);

我在尝试创建一个返回模板化类的模板化函数时注意到了这一点 .

template <typename...Arguments>
struct Testing {
  std::tuple<Arguments...> t;
  Testing(Arguments...args) : t(args...) {}
};

template <typename... Arguments>
Testing<Arguments...> create(Arguments... args) {
  return Testing<Arguments...>(args...);
}

在这种情况下,这将工作:

auto t4 = create(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);

这不会:

auto t5 = create(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);

1 回答

  • 12

    问题不是 make_tuple ,而是libstdc(gcc4.8.2)中 tuple 的移动构造函数 .

    对于类模板,成员函数仅在使用时实例化 . noexcept规范延迟类似,参见例如CWG issue 1330 .

    当从 make_tuple 初始化变量时,移动构造函数被实例化,即使它被省略(例如,检查它是否是不正确的) . 这就是为什么你看到只定义 tuple 变量和使用 make_tuple 之间的区别 .

    移动构造函数具有递归实现的条件 noexcept . 因此,对于每个模板参数,需要恒定数量的附加实例化 . 当超过最大实例化深度时,clang的错误输出的摘录:(支撑自己,文本墙进入)

    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:803:24: note: in instantiation of default argument for '__test, std::_Tuple_impl &&>' required here
          static true_type __test(int);
                           ^~~~~~~~~~~
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:803:24: note: while substituting deduced template arguments into function template '__test' [with _Tp = std::_Tuple_impl, _Arg = std::_Tuple_impl &&, $2 = ]
          static true_type __test(int);
                           ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:117:14: note: in instantiation of template class 'std::__is_direct_constructible_impl, std::_Tuple_impl &&>' requested here
        : public conditional::type
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:818:14: note: in instantiation of template class 'std::__and_ >, std::__is_direct_constructible_impl, std::_Tuple_impl &&> >' requested here
        : public __and_,
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:896:14: note: in instantiation of template class 'std::__is_direct_constructible_new_safe, std::_Tuple_impl &&>' requested here
        : public conditional::value,
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:904:39: note: in instantiation of template class 'std::__is_direct_constructible_new, std::_Tuple_impl &&>' requested here
        : public integral_constant, std::_Tuple_impl &&>' requested here
        : public __is_direct_constructible
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:956:39: note: in instantiation of template class 'std::__is_constructible_impl, std::_Tuple_impl &&>' requested here
        : public integral_constant, std::_Tuple_impl &&>' requested here
        : public conditional::type
                             ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1042:14: note: in instantiation of template class 'std::__and_, std::_Tuple_impl &&>, std::__is_nt_constructible_impl, std::_Tuple_impl &&> >' requested here
        : public __and_,
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1073:14: note: in instantiation of template class 'std::is_nothrow_constructible, std::_Tuple_impl &&>' requested here
        : public is_nothrow_constructible
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1079:14: note: in instantiation of template class 'std::__is_nothrow_move_constructible_impl, false>' requested here
        : public __is_nothrow_move_constructible_impl
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:117:14: note: in instantiation of template class 'std::is_nothrow_move_constructible >' requested here
        : public conditional::type
                 ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/tuple:268:16: note: in instantiation of template class 'std::__and_, std::is_nothrow_move_constructible > >' requested here
          noexcept(__and_,
                   ^
    /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:802:24: note: in instantiation of exception specification for '_Tuple_impl' requested here
                 = decltype(::new _Tp(declval()))>
    

    我们在这里可以看到实施例如 is_nothrow_move_constructibleis_nothrow_move_constructible ,以 __is_nt_constructible 实现,依此类推15个实例化级别 . 它打印得像一个调用堆栈,因此您可以从底部开始执行实例化 .


    这意味着 tuple 的每个模板参数都需要15个额外的实例化级别来进行此检查 . 最重要的是,总是需要9个级别(恒定深度) .

    因此,17个参数要求实例化深度为17 * 15 9 == 264 .

相关问题