【问题标题】:Why is the linker not complaining about multiple function definitions (only with templated functions)?为什么链接器不抱怨多个函数定义(仅使用模板函数)?
【发布时间】:2016-01-15 19:44:14
【问题描述】:

pid.h

#include <iostream>

template <class T>
void f(T t);

pid.c

#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);

main.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 中的函数设为非模板化,我会得到多重定义错误。

【问题讨论】:

  • 猜测,可能是编译器在使用“多个函数定义”这个短语时所暗示的多个函数定义。不过,只是猜测。
  • 顺便说一句,在标头中包含&lt;iostream&gt; 是一种不好的做法,因为这会强制客户端代码支持标头的整个复杂性。相反,在实际可行的情况下,请使用&lt;iosfwd&gt;
  • @Cheersandhth.-Alf 我只是通过一个例子来说明一点;但无论如何感谢您的评论。
  • 这可能取决于用于编译和链接代码的特定命令。你能提供这些信息吗?
  • 如果你在命令行切换pid.opid2.o的顺序,我希望你得到不同的输出。是这样吗?

标签: c++ templates


【解决方案1】:

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

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

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

在任何情况下,您通过为同一模板函数提供两个不同的定义来调用未定义的行为,因此当结果不符合您的期望时您不会抱怨:-)

编辑

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

正如@jwd 在 cmets 中指出的那样,在理想情况下,链接器能够诊断出您最初违反 ODR 的情况,但这样做实际上并不可行,除非您使用诸如整个程序优化之类的东西。

【讨论】:

  • 我看到链接器或编译器(不记得)抱怨多个定义,那什么时候会发生呢?另外,如果我将上述函数设为非模板化,则会出现多重定义错误(将更新问题)
  • 补充一点:在完美的世界中,您可能会收到有关您所做的坏事的警告。但是您使用的工具链(在这种情况下为 gcc)没有义务提供这样的警告,与 C++ 标准有关。
  • @Kam 非模板,非内联函数未标记为 weak,因此如果两个不同的目标文件中有两个具有相同签名的定义,链接器会报错.那可能是你见过的地方?
猜你喜欢
  • 2011-12-14
  • 1970-01-01
  • 2015-10-08
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 2012-06-29
  • 1970-01-01
  • 2014-01-29
相关资源
最近更新 更多