为什么链接器不抱怨多个函数定义(仅适用于模板化函数)

Why is the linker not complaining about multiple function definitions (only with templated functions)?

本文关键字:函数 适用于 定义 为什么 链接      更新时间:2023-10-16

pid.h

#include <iostream>
template <class T>
void f(T t);

皮德·

#include "pid.h"
template <class T>
void f(T t) {
  std::cout << t;
}
template
void f<int>(int);

PID2.c

#include "pid.h"
template <class T>
void f(T t) {
  std::cout << 55;
}
template
void f<int>(int);

主.c

#include "pid.h"
int main()
{
  f(1);
  return 0;
}

命令:

g++ "-IC:\Users\kam\workspace\boost_1_56_0" -O0 -g3 -pg -Wall -c -fmessage-length=0 -std=c++11 -pthread -o "src\pid2.o" "..\src\pid2.cpp" 
g++ "-IC:\Users\kam\workspace\boost_1_56_0" -O0 -g3 -pg -Wall -c -fmessage-length=0 -std=c++11 -pthread -o "src\pid.o" "..\src\pid.cpp" 
g++ "-IC:\Users\kam\workspace\boost_1_56_0" -O0 -g3 -pg -Wall -c -fmessage-length=0 -std=c++11 -pthread -o "src\main.o" "..\src\main.cpp" 
g++ -pg -o Hello_World.exe "src\pid2.o" "src\pid.o" "src\main.o"  

结果:

55

现在,如果我使 pid.c 和 pid2.c 中的函数非模板化,我确实会得到多重定义错误。

我猜链接器没有抱怨,因为它不知道它正在发生。

在翻译单元中编译函数模板的特定实例化时,这些函数被标记为弱符号(使用 ELF 术语(;当链接器看到多个具有相同签名的弱符号时,它会删除除一个定义之外的所有定义。

C++标准要求内联/模板函数的多个定义是相同的 - 著名的一个定义规则。这就是为什么模板和内联函数应始终在头文件中定义的原因。链接器不需要实际检查函数定义是否相同 - 它假定您遵守了规则,因此它可以选择其中任何一个。在这种情况下,它选择了输出55的版本,但它可以很容易地选择另一个。

在任何情况下,您

都通过提供同一模板函数的两个不同定义来调用未定义的行为,因此当结果与您的期望不符时,您不会抱怨:-(

编辑

但是,非模板、非内联函数在编译时不会标记为函数。在这种情况下,只有一个翻译单元(即一个 cpp 文件(可以提供定义。如果您尝试在两个不同的对象文件中定义相同的非模板、非内联函数,则链接器将看到多个"强"定义并给出错误。

正如@jwd在评论中指出的那样,在理想的世界中,链接器将能够诊断您最初违反 ODR 的情况,但除非您使用全程序优化之类的东西,否则这样做并不可行。