7

为什么这行得通?

我看到类似的 SO 问题说明它确实如此,但有人可以更详细地解释它吗?特别是,这种行为是否受标准保护?

#ifndef I_H_
#define I_H_

typedef void (*FuncPtr)();

template<typename T>
void FuncTemplate() {}

class C {};

#endif

cc

#include "i.h"

FuncPtr a() {
  return &FuncTemplate<C>;
}

b.cc

#include "i.h"

FuncPtr b() {
  return &FuncTemplate<C>;
}

mcc

#include <iostream>

#include "i.h"

FuncPtr a();
FuncPtr b();

int main() {
  std::cout << (a() == b() ? "equal" : "not equal") << std::endl;

  return 0;
}

然后

$ g++ -c -o a.o a.cc
$ g++ -c -o b.o b.cc
$ g++ -c -o m.o m.cc
$ g++ a.o b.o m.o -o prog
$ ./prog
equal

折腾-Wall -Wextra -Werror -ansi所有的g++电话都会产生相同的结果。

我的(天真的)理解是在每个和编译单元FuncTemplate中实例化一次,因此地址应该每个指向一个副本。毕竟这些结果如何相同,这种行为是可移植的还是受保护的?a.ob.o

编辑共享库案例:

$ g++ -shared -o liba.so a.cc
$ g++ -shared -o libb.so b.cc
$ g++ -c -o m.o m.cc
$ g++ -L. -la -lb m.o -o prog
$ ./prog
equal
4

2 回答 2

6

这包含在一个定义规则中:

3.2 一个定义规则[basic.def.odr]

第 5 段:

类类型(第 9 条)、枚举类型(7.2)、带外部链接的内联函数(7.1.2)、类模板(第 14 条)、非静态函数模板(14.5.6)可以有多个定义、类模板的静态数据成员 (14.5.1.3)、类模板的成员函数 (14.5.1.1) 或在程序中未指定某些模板参数的模板特化 (14.7、14.5.5),前提是每个定义出现在不同的翻译单元中,并且定义满足以下要求。给定这样一个名为 D 的实体在多个翻译单元中定义,则

有一系列必须遵守的标准或其未定义的行为。在上面这些确实成立。然后 ...

如果 D 的定义满足所有这些要求,那么程序的行为就好像有一个 D 的定义。

因此,从技术上讲,您可以在每个翻译单元中拥有该功能的副本。

它看起来像最后一个短语中的措辞,但要求它们的行为都相同。这意味着获取这些对象中的任何一个的地址都应该得到相同的地址。

于 2011-10-06T05:19:53.890 回答
4

这是由标准保证的,因为它不违反单一定义规则。本质上,如果一个内联函数或模板函数的声明和定义在多个翻译单元中是相同的,那么程序应该表现得好像它有一个定义,当被采用时,它会扩展到它的地址。请参阅我对另一个涉及模板类的静态成员的问题的回答。

至于标准的相关部分[basic.def.odr]

...如果 D 的定义满足所有这些要求,那么程序的行为就好像有一个 D 的定义。如果 D 的定义不满足这些要求,那么行为是未定义的。

于 2011-10-06T05:19:36.603 回答