我正在使用c ++ 11可变参数模板,但是编译器抱怨模板实例化深度最大为900,代码如下:
template<typename F1, typename F2>
composed<F1, F2> compose(F1 f1, F2 f2) {
return composed<F1, F2>(f1, f2);
}
template<typename F1, typename F2, typename... Fs>
auto compose(F1 f1, F2 f2, Fs... fs) ->decltype(compose(compose(f1, f2), fs...)) {
return compose(compose(f1, f2), fs...);
}
我正在使用此模板,例如:
auto composed_func = compose(f1, f2, f3, f4);
但是如果我将可变参数模板的定义更改为:
template<typename F1, typename F2, typename F3, typename... Fs>
auto compose(F1 f1, F2 f2, F3 f3, Fs... fs) ->decltype(compose(compose(f1, f2), f3, fs...)) {
return compose(compose(f1, f2), f3, fs...);
}
它将正常运行。 我不清楚为什么会这样。在我看来,上限用法看起来也是有效的,因为它仍递归地减少了用于调用compose的args。
答案 0 :(得分:3)
您在第二个函数中具有无限递归:它不像您想象的那样调用第一个函数,而是调用自身:
template<typename F1, typename F2, typename... Fs>
auto compose(F1 f1, F2 f2, Fs... fs) ->decltype(compose(compose(f1, f2), fs...)) {
return compose(compose(f1, f2), fs...);
}
假设您致电:
compose(f, g, h);
这将使用F1 = decltype(f)
,F2 = decltype(g)
和Fs = {decltype(h)}
调用该函数。然后该函数继续并调用:
compose(f1, f2)
实际上是:
compose(f, g);
这将使用F1 = decltype(f)
,F2 = decltype(g)
和空的Fs = {}
调用相同的函数。基本上执行:
compose(compose(f, g));
这又用F1 = decltype(f)
,F2 = decltype(g)
和空的Fs = {}
进行了调用。然后一直到无穷大,或者直到达到900的限制。
您需要确保仅在f2
之后有第二个功能时,第二个功能才相关:
template<typename F1, typename F2, typename F3, typename... Fs>
auto compose(F1 f1, F2 f2, F3 f3, Fs... fs) ->decltype(compose(compose(f1, f2), f3, fs...)) {
return compose(compose(f1, f2), f3, fs...);
}
答案 1 :(得分:2)
您可以通过以下方式修复它:
template<typename F1, typename F2>
composed<F1, F2> compose(F1 f1, F2 f2) {
return composed<F1, F2>(f1, f2);
}
template<typename F1, typename F2, typename... Fs>
auto compose(F1 f1, F2 f2, Fs... fs)
->decltype(compose(::compose(f1, f2), fs...))
{
return compose(compose(f1, f2), fs...);
}
ADL可以找到不合格的名称,并且在实例化时进行搜索。
因此,通过不合格的查找,我们首先生成了候选对象(即使它们不会是更好的匹配)
compose(f, g)
可以是:
template<typename F1, typename F2> compose
template<typename F1, typename F2, typename...Fs> compose
。 (带有FS
个空包)对于后一种情况,我们有decltype(compose(compose(f, g)))
可以解决,因此再次compose(f, g)
->无限递归。
另一方面,将立即搜索合格名称,并且只能找到完全声明的函数(而不是其本身,因为尾随返回类型是声明的一部分)。
因此::compose
避免将自己视为过载的候选者。