模板重载会导致链接器错误/奇怪的行为

template overloading results in linker error / strange behaviour

本文关键字:错误 重载 链接      更新时间:2023-10-16

使用以下最小示例,我在 Visual Studio 15.8.7 中的本地系统上收到链接器错误(具有标准设置的标准控制台应用程序(刚刚删除了预编译标头((:"错误LNK1179无效或损坏的文件:重复的 COMDAT '??$f@H@@YAXH@Z'">

#include <cstdio>
template<typename T> void f(T) { printf("1"); }  //#1. T can be deduced
template<typename T> void f(int) { printf("2"); } // #2. T needs to be specified explicitly
int main()
{
f(8); // a) calls #1
f<int>(8); // b) calls #2           
}
  • 注释掉调用 a( 或调用 b( 将导致成功链接。独立调用 a( 调用模板定义 #1。第二个调用 b( 调用模板定义 #2。不出所料。
  • 在发布模式下生成成功。输出为 11。因此,这两个调用都调用模板定义 #1。意外。ODR违规?
  • 此外,我观察到以下奇怪的行为(在调试设置中(:
    1. 我注释掉模板定义 #2
    2. 我进行全面重建
    3. 我在模板定义 #2 中评论
    4. 我建造(不是重建,只是建造(
    5. 构建成功
    6. 输出为 11 而不是 12

增量链接做奇怪的事情?

在 wandbox、godbolt 和 coliru 上,我可以编译、链接和运行,并获得 gcc 和 clang 的预期行为。

项目符号 3 中描述的观察结果使我认为这与增量链接有关。但也许代码也没有很好地定义?在学习 https://en.cppreference.com/w/cpp/language/function_template 时,我遇到了以下情况:

函数上调用两个涉及模板参数的表达式 等效,如果它们不等价,但对于任何给定的集合 模板参数,两个表达式的求值结果为 相同的值。

如果程序包含函数模板的声明,这些函数模板是 功能等效但不等效,程序格式不正确; 无需诊断。

那么,上面的代码是不是格式不正确?我是否违反了 ODR?还是一切都很好,它只是一个链接器/编译器错误?

编辑:固定点 3. 我在定义 #2 或课程中发表评论。

更新:新的一天,我想问题解决了。今天我无法重现该问题。我没有更改系统上的任何内容。我刚刚启动,打开我的项目,它像预期的那样工作。不知道发生了什么。但与学习新的特殊神秘模板重载规则相比,这种方式更好:-P

程序很好。您有两个不同的函数正在被调用。这是一个编译器和/或链接器错误。

[temp.over.link] 引出:

可以重载函数模板,以便两个不同的函数模板专用化具有相同的类型。

[ 示例:

// translation unit 1:
template<class T>
void f(T*);
void g(int* p) {
f(p); // calls f<int>(int*)
}
// translation unit 2:
template<class T>
void f(T);
void h(int* p) {
f(p); // calls f<int*>(int*)
}

— 结束示例 ]

这种专业化是不同的功能,不违反单一定义规则。

您有两个不同的函数模板,句号。关于等价的措辞与模板参数和依赖表达式有关 ([temp.over.link]/5(:

涉及模板参数的两个表达式被视为等效,如果 [...]

int不涉及模板参数,因此绝不能将其视为等同于T