为什么这样做?
我看到类似的SO问题说它确实存在,但有人可以更详细地解释一下吗?特别是,这种行为是否受到标准的保护?
i.h
#ifndef I_H_
#define I_H_
typedef void (*FuncPtr)();
template<typename T>
void FuncTemplate() {}
class C {};
#endif
a.cc
#include "i.h"
FuncPtr a() {
return &FuncTemplate<C>;
}
b.cc
#include "i.h"
FuncPtr b() {
return &FuncTemplate<C>;
}
m.cc
#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.o
和b.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
答案 0 :(得分:6)
这包含在一个定义规则中:
类类型(第9条),枚举类型(7.2),带内部链接的内联函数(7.1.2),类模板(第14条),非静态函数可以有多个定义template(14.5.6),类模板的静态数据成员(14.5.1.3),类模板的成员函数(14.5.1.1),或未指定某些模板参数的模板特化(14.7, 14.5.5)在一个程序中,每个定义出现在不同的翻译单元中,并且定义满足以下要求。鉴于在多个翻译单元中定义了这样一个名为D的实体,那么
以下是必须遵守的标准列表或未定义的行为。在上面这些确实成立。然后......
如果D的定义满足所有这些要求,则程序的行为应该像D的单一定义一样。
从技术上讲,您可以在每个翻译单元中获得该功能的副本。
看起来在最后一个短语中的措辞使得它们都要求它们都表现相同。这意味着获取任何这些对象的地址应该产生相同的地址。
答案 1 :(得分:4)
这是标准保证的,因为它不违反一个定义规则。实质上,如果内联函数或模板函数的声明和定义在多个转换单元中是相同的,则程序应该表现得就像它有一个定义,它延伸到它的地址。 See my answer to another question that involved static members of template classes.
至于标准[basic.def.odr]
的相关部分:
......如果D的定义满足所有这些要求,那么 程序应该表现得好像有一个单一的定义D.如果 D的定义不满足这些要求,那么行为 未定义。