c ++可变参数模板的实例化深度超过最大900

时间:2018-09-06 03:44:07

标签: c++ c++11 variadic-templates trailing-return-type

我正在使用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。

2 个答案:

答案 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...);
}

Demo

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避免将自己视为过载的候选者。