【问题标题】:C++ compiler optimizations discard template specializationsC++ 编译器优化丢弃模板特化
【发布时间】:2014-02-21 13:57:21
【问题描述】:

我注意到如果启用编译器优化,.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++ 会产生相同的结果。

【问题讨论】:

  • 问题是 main.cpp 不知道 Obj 特化存在,因为它在编译时被解决,这就是为什么板倾向于放在标题中
  • 两个编译器给出相同的结果并不奇怪,也不表示已定义的行为。有时,常见的实现策略使某些类型的 UB 表现得有点可预测。编译器编写者并没有竭尽全力以最壮观的方式使 UB 崩溃的程序。
  • 添加到 Mark 的评论中,事实上,C++ 要求所有特化在使用它们的任何地方都显式声明(但不一定定义),因此您可以将定义保留在 templates.cpp 中,但是您确实需要在标题中声明专业化。

标签: c++ templates template-specialization


【解决方案1】:

使用在 POC(调用点)不可见的特化是错误

顺便说一句,编译器不需要将其视为错误并可以按照自己的意愿进行处理。

您应该对头文件进行专门化,但在 C++11 中,您可以将其放入外部单元 with the C++11 "extern" keyword for explicit instantiation declaration

Sebastian 指出:这违反了14.7.3/6 中的要求:

“如果一个模板 [...] 是明确特化的,那么 专业化应在第一次使用之前声明 会导致隐式实例化的特化 放置在发生这种使用的每个翻译单元中;不 需要诊断。”

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

【讨论】:

  • 具体来说,这违反了 14.7.3/6 中的要求:“如果模板 [...] 是明确特化的,则应在第一次使用该特化之前声明该特化导致在发生这种使用的每个翻译单元中发生隐式实例化;不需要诊断。”由于不需要诊断,因此违反此要求是未定义的行为。
【解决方案2】:

与任何函数一样,必须在使用之前在每个使用它的翻译单元中声明特化。否则,编译器不知道它的存在,并会使用泛型模板(必要时实例化它)。

这可能意味着您最终会得到两个不同的特化副本(一个是您编写的,一个是从模板实例化的);取决于链接器的确切作用,这可能会导致调用其中一个或另一个,或者链接错误或其他一些未定义的行为。

请注意,您不需要将专业化的定义移动到标题中;声明就够了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 2012-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多