C++ 编译器优化丢弃模板特化

标签 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>
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

如 Sebastian 所述:这违反了 14.7.3/6 中的要求:

"If a template [...] is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required."

由于不需要诊断,违反此要求是未定义的行为

关于C++ 编译器优化丢弃模板特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21936455/

相关文章:

c++ - 延迟评估和 const 正确性问题

c++ - 重载成员函数的 decltype

c++ - 具有相同名称的不同功能模板之间的重载优先级

c++ - GCC:由于数据类型的范围有限,比较总是正确的——在模板参数中?

C++模板函数,替换失败跳过实现

c++ - 如何将枚举类声明为模板类的内部类?

c++ - Qt undefined reference

c++ - 无法从 XXX** 转换为常量 XXX**

java - 对不同数据类型的操作

C++如何用模板特化或其他方式实现?