我注意到如果启用编译器优化,.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/

10-11 23:07
查看更多