【发布时间】:2014-02-21 13:57:21
【问题描述】:
我注意到如果启用编译器优化,.cpp 文件中的模板特化会被丢弃。我在一个大型应用程序中发现了这一点,并将问题归结为一个简单的示例。
首先,我在 obj.h 中定义了一个新类
#ifndef _OBJ_H_
#define _OBJ_H_
class Obj { };
#endif //_OBJ_H_
然后我在templates.h中定义了一个新的模板函数
#ifndef _TEMPLATES_H_
#define _TEMPLATES_H_
template<typename T>
int get()
{
return 0;
}
#endif //_TEMPLATES_H_
...以及 templates.cpp 中 Obj 类的特化
#include "templates.h"
#include "obj.h"
template<>
int get<Obj>()
{
return 1;
}
然后我从 main 调用函数:
#include <stdio.h>
#include "templates.h"
#include "obj.h"
int main()
{
printf("Get: %d\n", get<Obj>());
return 0;
}
使用不同的-O 级别编译此示例会产生不同的输出。
$ g++ -o a main.cpp templates.cpp -O0
$ ./a
Get: 1
$ g++ -o a main.cpp templates.cpp -O2 #same with -O3, -O4, Os
$ ./a
Get: 0
用 clang 替换 g++ 也会发生同样的情况。我正在使用 g++ 4.7.2 和 clang 3.4。
我不是汇编专家,但查看生成的代码我可以看到-O0 版本定义了重整符号_Z3getI3ObjEiv,它指的是专业化,而优化版本只是内联所有内容(如我所料)。
问题最终解决了,将所有特化移动到头文件,但我仍然很好奇:为什么会发生这种情况?最初我认为我遇到了未定义的行为,尽管奇怪的是,如果是这种情况,clang 和 g++ 会产生相同的结果。
【问题讨论】:
-
问题是 main.cpp 不知道 Obj 特化存在,因为它在编译时被解决,这就是为什么板倾向于放在标题中
-
两个编译器给出相同的结果并不奇怪,也不表示已定义的行为。有时,常见的实现策略使某些类型的 UB 表现得有点可预测。编译器编写者并没有竭尽全力以最壮观的方式使 UB 崩溃的程序。
-
添加到 Mark 的评论中,事实上,C++ 要求所有特化在使用它们的任何地方都显式声明(但不一定定义),因此您可以将定义保留在 templates.cpp 中,但是您确实需要在标题中声明专业化。
标签: c++ templates template-specialization