【问题标题】:Code not compiling when template functions are placed in a certain order模板函数按特定顺序放置时代码无法编译
【发布时间】:2022-08-23 02:12:16
【问题描述】:

以下程序编译成功。

template<typename T>
T sum(T x) {
    return x;
}

template<typename T, typename... Args>
T sum(T x, Args... args) {
    return x + sum(args...);
    
}

int main() {
    sum(1, 2, 3, 4, 5);

}

但是,当我切换模板函数的编写顺序时,它不再编译:

template<typename T, typename... Args>
T sum(T x, Args... args) {
    return x + sum(args...);

}

template<typename T>
T sum(T x) {
    return x;
}

int main() {
    sum(1, 2, 3, 4, 5);

}

我得到的编译器错误是:

错误 C2780:\'T sum(T,Args...)\':需要 2 个参数 - 提供了 0,消息:请参阅 \'sum\' 的声明

为什么会这样?在 main() 中调用之前不是已经定义了这两个函数吗?为什么它们的书写顺序很重要?

  • 你的编译器错误是什么?
  • @jkb 错误 C2780:\'T sum(T,Args...)\':需要 2 个参数 - 提供了 0,消息:请参阅 \'sum\' 的声明
  • 您的单个 arg sum 是终止递归的原因。
  • 在非编译情况下,当定义第一个模板函数时,没有其他sum(),因此它是明确的。在编译的情况下现在有两个sum()s,所以重载分辨率进入图片。规则相当晦涩难懂,但这就是胶囊摘要。
  • 这不是问题,但如果您可以访问 C++17 编译器,则可以使用折叠并将 sum 作为单个函数实现。 gcc.godbolt.org/z/M9caYv163

标签: c++ templates variadic-templates


【解决方案1】:

您问题的核心是以下问题:当函数名称(例如 sum)出现在函数模板中时,它指的是什么函数?

你写了:

在 main() 中调用之前不是已经定义了这两个函数吗?为什么它们的书写顺序很重要?

您似乎有以下期望:当一个函数模板是实例化,并且其中出现了诸如sum之类的函数名称,编译器将找到迄今为止已声明的sum的所有重载。因此,根据您的理论,由于sum 函数模板在main 中调用之前不会被实例化,因此sum 的两个重载都是可见的,因此对sum 的递归调用应该考虑两者重载并最终选择一元终止递归。

但是,正如您所见,情况并非如此。如果首先声明可变参数sum,它最终会使用单个参数递归调用自身(忽略稍后声明的一元sum 重载),然后尝试调用sum()(无参数),从而导致编译错误。

因此,在本例中,当编译器在函数模板中看到对sum 的调用时,它只会找到在调用点声明的名为sum 的函数。当你改变顺序时,你做到了,可变参数sum 永远不能调用一元sum

但实际上,真相要复杂得多。 C++ 标准需要一种通俗地称为“两阶段查找”的行为。这意味着:

  1. 当编译器看到函数模板的定义时,它会找到到目前为止已经声明的所有名为sum的函数,并且
  2. 当编译器稍后实例化函数模板时,它使用argument-dependent lookup 来查找sum 的其他重载,无论它们是在封闭模板之前还是之后声明的。

    在您的代码中,sum 只会使用不属于任何命名空间的int 类型的参数调用。因此,可变参数 sum 仅在阶段 1 中发现自己,而在阶段 2 中什么也没有。

【讨论】:

    猜你喜欢
    • 2020-07-21
    • 2023-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-02
    相关资源
    最近更新 更多