Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;编译器优化放弃模板专门化_C++_Templates_Template Specialization - Fatal编程技术网

C++ C++;编译器优化放弃模板专门化

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

我注意到,如果启用编译器优化,则.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_
用叮当声替换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