C++ C++;编译器优化放弃模板专门化
我注意到,如果启用编译器优化,则.cpp文件中的模板专门化将被丢弃。我在一个大型应用程序中发现了这一点,并将问题归结为一个简单的示例 首先,我在obj.h中定义了一个新类C++ C++;编译器优化放弃模板专门化,c++,templates,template-specialization,C++,Templates,Template Specialization,我注意到,如果启用编译器优化,则.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&g
#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_
用叮当声替换g++也会发生同样的情况。我正在使用g++4.7.2和Clang3.4。我不是汇编专家,但通过查看生成的代码,我可以看到
-O0
版本定义了损坏的符号\u zgeti3objeiv
,它表示专门化,而优化的版本只是内联所有内容(如我所料)。问题最终解决了,将所有的专门化移到头文件,但我仍然好奇:为什么会发生这种情况?起初,我认为我遇到了一个未定义的行为,但奇怪的是,如果是这样的话,clang和g++都会产生相同的结果。与任何函数一样,在使用它的每个翻译单元中,在使用它之前必须声明专门化。否则,编译器不知道它存在,并将使用通用模板(如有必要,对其进行实例化) 这可能意味着您最终会得到两个不同的专业化副本(一个是您编写的,另一个是从模板实例化的);这可能会导致一个或另一个被调用,或链接错误,或其他一些未定义的行为,具体取决于链接器的具体操作
请注意,您不需要将专业化的定义移到标题中;声明它就足够了。使用在POC调用点不可见的专门化是一个错误 顺便说一句,编译器不需要将此视为错误,可以随意处理 您应该在头文件中进行专门化,但在C++11中,您可以在外部单元中进行专门化 正如塞巴斯蒂安所指出的:这违反了
14.7.3/6
中的要求:
“如果模板[…]被明确专门化,那么
专业化应在首次使用前声明
将导致隐式实例化的专门化
在每一个出现这种用法的翻译单元中;没有
需要进行诊断。”
由于不需要诊断,违反此要求是未定义的行为问题在于main.cpp不知道Obj专业化的存在,因为它在编译时得到解决,这就是为什么,铭牌倾向于放置在头部,这并不特别奇怪,也不表示已定义的行为,哪两个编译器给出相同的结果。有时,通用的实现策略使某些类型的UB行为具有某种可预测性。不像编译器编写者正在以最引人注目的方式来制作UB崩溃的程序。为了添加到马克的评论中,C++实际上要求所有的专门化都被明确地声明(但不一定是定义的),它们可以被使用,因此您可以在TEMPPLACE.CPP中保留定义。但您确实需要在标题中声明专业化。具体来说,这违反了14.7.3/6中的要求:“如果模板[…]是明确专门化的,则该专门化应在第一次使用该专门化之前声明,该专门化将导致在发生此类使用的每个翻译单元中发生隐式实例化;不需要诊断。”由于不需要诊断,因此违反该要求是未定义的行为。
#include "templates.h"
#include "obj.h"
template<>
int get<Obj>()
{
return 1;
}
#include <stdio.h>
#include "templates.h"
#include "obj.h"
int main()
{
printf("Get: %d\n", get<Obj>());
return 0;
}
$ 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