我注意到如果启用编译器优化,.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++ 都会产生相同的结果。
最佳答案
使用在 POC 中不可见的 特化 - 调用点 - 是一个错误 。
顺便说一下,编译器不需要将此视为错误,并且可以随心所欲地处理它。
您应该对头文件进行专门化,但在 C++11 中,您可以将其放入外部单元 with the C++11 "extern" keyword for explicit instantiation declaration
正如塞巴斯蒂安所指出的:这违反了 14.7.3/6
的要求:
由于不需要诊断,因此 违反此要求是未定义的行为 。
关于C++ 编译器优化丢弃模板特化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21936455/