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

c++ variadic template instantiation depth exceeds maximum of 900

我正在使用 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...);
}

它将正常工作。 我不清楚为什么会这样。在我看来,上面的用法看起来也有效,因为它仍然递归地减少 args 以调用 compose。

你的第二个函数有无限递归:它没有像你想象的那样调用第一个函数,而是调用它自己:

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

您可以通过以下方式修复它:

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) -> 无限递归。

另一方面,立即搜索限定名称,并且只能找到完整声明的函数(因此不能找到它本身,因为结尾-return-类型是声明的一部分)。 所以 ::compose 避免将自己视为过载的候选对象。