模板重载导致链接器错误/奇怪的行为
template overloading results in linker error / strange behaviour
使用以下最小示例,我在 visual studio 15.8.7(具有标准设置的标准控制台应用程序(仅删除预编译头文件))中的本地系统上出现 linker 错误:"Error LNK1179 invalid or corrupt file: duplicate 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) 将导致成功 linking。独立调用 a) 调用模板定义#1。第二次调用 b) 调用模板定义#2。不出所料。
- 发布模式构建成功。输出为 11。因此两个调用都调用模板定义 #1。意外。 ODR 违规?
- 此外,我观察到以下奇怪的行为(在调试设置中):
- 我注释掉模板定义#2
- 我做了一个完整的重建
- 我在模板定义#2 中回复评论
- 我构建(不是重建,只是构建)
- 构建成功
- 输出是 11 而不是 12
增量 link做奇怪的事情?
我可以在 wandbox、godbolt 和 coliru 上编译,link 和 运行 并使用 gcc 和 clang 获得预期的行为。
要点 3 中描述的观察结果让我认为它与增量 linking 有关。但也许代码也没有明确定义?在学习https://en.cppreference.com/w/cpp/language/function_template时遇到了以下情况:
Two expressions involving template parameters are called functionally
equivalent if they are not equivalent, but for any given set of
template arguments, the evaluation of the two expressions results in
the same value.
和
If a program contains declarations of function templates that are
functionally equivalent but not equivalent, the program is ill-formed;
no diagnostic is required.
那么,上面的代码是不是格式错误?我有 ODR 违规吗?或者一切都很好,只是一个 linker/compiler 错误?
编辑:固定点 3。我在定义 #2 或课程中回复评论。
更新: 新的一天,我想问题已经解决了。今天我无法重现这个问题。我没有更改系统上的任何内容。我刚启动,打开我的项目,它按预期工作。不知道怎么回事。但与学习新的特殊神秘模板重载规则相比,这种方式更好:-P
程序没问题。您有两个 不同的 函数被调用。这是编译器 and/or 链接器错误。
[temp.over.link] 领先于:
It is possible to overload function templates so that two different function template specializations have the same type.
[ Example:
// 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*)
}
— end example ]
Such specializations are distinct functions and do not violate the one-definition rule.
你有两个不同的功能模板,句号。关于等价的措辞与模板参数和依赖表达式有关([temp.over.link]/5):
Two expressions involving template parameters are considered equivalent if [...]
int
不涉及模板参数,因此不能认为它等同于 T
.
使用以下最小示例,我在 visual studio 15.8.7(具有标准设置的标准控制台应用程序(仅删除预编译头文件))中的本地系统上出现 linker 错误:"Error LNK1179 invalid or corrupt file: duplicate 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) 将导致成功 linking。独立调用 a) 调用模板定义#1。第二次调用 b) 调用模板定义#2。不出所料。
- 发布模式构建成功。输出为 11。因此两个调用都调用模板定义 #1。意外。 ODR 违规?
- 此外,我观察到以下奇怪的行为(在调试设置中):
- 我注释掉模板定义#2
- 我做了一个完整的重建
- 我在模板定义#2 中回复评论
- 我构建(不是重建,只是构建)
- 构建成功
- 输出是 11 而不是 12
增量 link做奇怪的事情?
我可以在 wandbox、godbolt 和 coliru 上编译,link 和 运行 并使用 gcc 和 clang 获得预期的行为。
要点 3 中描述的观察结果让我认为它与增量 linking 有关。但也许代码也没有明确定义?在学习https://en.cppreference.com/w/cpp/language/function_template时遇到了以下情况:
Two expressions involving template parameters are called functionally equivalent if they are not equivalent, but for any given set of template arguments, the evaluation of the two expressions results in the same value.
和
If a program contains declarations of function templates that are functionally equivalent but not equivalent, the program is ill-formed; no diagnostic is required.
那么,上面的代码是不是格式错误?我有 ODR 违规吗?或者一切都很好,只是一个 linker/compiler 错误?
编辑:固定点 3。我在定义 #2 或课程中回复评论。
更新: 新的一天,我想问题已经解决了。今天我无法重现这个问题。我没有更改系统上的任何内容。我刚启动,打开我的项目,它按预期工作。不知道怎么回事。但与学习新的特殊神秘模板重载规则相比,这种方式更好:-P
程序没问题。您有两个 不同的 函数被调用。这是编译器 and/or 链接器错误。
[temp.over.link] 领先于:
It is possible to overload function templates so that two different function template specializations have the same type.
[ Example:
// 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*) }
— end example ]
Such specializations are distinct functions and do not violate the one-definition rule.
你有两个不同的功能模板,句号。关于等价的措辞与模板参数和依赖表达式有关([temp.over.link]/5):
Two expressions involving template parameters are considered equivalent if [...]
int
不涉及模板参数,因此不能认为它等同于 T
.