为什么这超过了递归模板的最大深度

Why does this exceed the maximum recursive template depth?

本文关键字:深度 过了 递归 为什么      更新时间:2023-10-16

我一直在玩可变模板,并注意到以下内容。

这很好:

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);

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

对于类模板,成员函数仅在使用时实例化。noexcept规范也有类似的延迟,例如参见CWG问题1330。

当从make_tuple初始化变量时,move构造函数会被实例化,即使它被省略了(例如,检查它是否格式错误)。这就是为什么您会看到仅定义tuple变量使用make_tuple之间的差异。

move构造函数有一个递归实现的条件noexcept。因此,对于每个模板参数,都需要恒定数量的额外实例化。clang++在超过最大实例化深度时的错误输出摘录:(支撑自己,文本墙传入)

/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:803:24:注意:在实例化'__test的默认参数时,此处需要std::_Tuple_impl&&>'static true_type __test(int);^~~~~~~~~~~/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:803:24:注意:将推导出的模板参数替换为函数模板'__test'[其中_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:注意:在此处请求的模板类"std::__is_direct_constructible_impl,std::_Tuple_impl&&>"的实例化中:public条件::type^/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:818:14:注意:在此处请求的模板类"std::__and_>,std::___is_direct_constructible_impl,std:::_Tuple_impl&&>>"的实例化中:公共__和_,^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:896:14:注意:在此处请求的模板类"std::__is_direct_constructible_new_safe,std::_Tuple_impl&&>"的实例化中:public条件::value,^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:904:39:注意:在此处请求的模板类"std::__is_direct_constructible_new,std::_Tuple_impl&&>"的实例化中:public integral_content,此处请求std::_Tuple_impl&&>':公共__is_direct_constructible^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:956:39:注意:在此处请求的模板类"std::__is_constructible_impl,std::_Tuple_impl&&>"的实例化中:public integral_content,此处请求std::_Tuple_impl&&>':public条件::type^/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:1042:14:注意:在此处请求的模板类"std::__and_,std::_Tuple_impl&&>,std:"__is_nt_constructionble_impl,std:"_Tuple_idmpl&&>>的实例化中:公共__和_,^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:1073:14:注意:在此处请求的模板类"std::is_nothrow_constructible,std::_Tuple_impl&&>"的实例化中:public is_nothrow_constructible^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:1079:1079:14:注意:在此处请求的模板类"std::__is_nothrow_move_constructible_impl,false>"的实例化中:公共__is_nothrow_move_constructible_impl^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:117:14:注意:在此处请求的模板类"std::is_nothrow_move_constructible>"的实例化中:public条件::type^//usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/tuple:268:16:注意:在此处请求的模板类"std::__and_,std::is_nothrow_move_constructible>>"的实例化中无例外(__and_,^/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../include/c++/4.8/type_traits:802:24:注意:在此处请求的'_Tuple_impl'异常规范的实例化中=decltype(::new _Tp(declval()))>

我们可以在这里看到例如is_nothrow_move_constructibleis_nothrow_constructible方面的实现,它在__is_nt_constructible等方面实现,用于15个实例化级别。这就像一个调用堆栈一样打印,所以您可以从底部开始执行实例化。


这意味着tuple的每个模板参数都需要15个额外的实例化级别来进行此检查。除此之外,始终需要9个级别(恒定深度)。

因此,17个参数需要17*15+9==264的实例化深度。