【问题标题】:different behavior depending on the optimization options不同的行为取决于优化选项
【发布时间】:2015-02-06 00:56:18
【问题描述】:

我发现了一个示例,其中输出因优化设置而异(-O3 与无),但 GCC 4.8.2 不会产生警告,即使使用 -std=c++11 -Wall -pedantic 选项也是如此。

在这种特殊情况下,我假设“忘记”header.h 中的注释行是一个错误,而对于 -O3c<int>::get() 会被内联。

但是,有什么方法可以保护自己免受此类错误的影响——也许是编译器或链接器选项?

header.h:

#ifndef HEADER_H
#define HEADER_H

template<class T>
struct c
{
   int get() { return 0; }
};

// template<> int c<int>::get();

#endif

imp.cpp:

#include "header.h"

template<> 
int c<int>::get()
{
   return 1;
}

main.cpp:

#include <iostream>
#include "header.h"

int main()
{
    c<int> i;
    std::cout << i.get() << '\n'; // prints 0 with -O3, and 1 without
}

构建:

c++ -std=c++11 -pedantic -Wall -O3 -c imp.cpp
c++ -std=c++11 -pedantic -Wall -O3 -c main.cpp
c++ -std=c++11 -pedantic -Wall -O3 imp.o main.o

【问题讨论】:

  • 行为有何不同?

标签: c++ templates c++11 gcc


【解决方案1】:
  1. 如果您在头文件中有该行,您将得到的就是该成员函数的显式特化声明。

    因此,main.cpp 可以确保在其他编译单元中进行定义,并且一切正常。

  2. 如果您忽略它,则违反了 ODR:

    您的类模板的特定实例化在编译单元中是不同的。导致“格式错误,不需要诊断”,所以一切正常。

    并且(目前?)没有编译器选项可以强制 gcc 对其进行诊断。

【讨论】:

    【解决方案2】:

    这里真正的错误是您布置源文件和构建它们的方式。当使用c&lt;int&gt;::get() 时,它的定义应该可用以便实例化模板。为了解决这个问题,header.h 应该#include "imp.cpp" 而不是相反。您可能需要将 imp.cpp 重命名为 imp.inl 或其他名称。

    当您定义在单个 .cpp 文件之外使用的模板时,任何包含其标题的人都应该可以看到这些定义。

    顺便说一句:我认为没有任何方法可以让编译器或链接器警告您在这里所做的事情。但是如果你按照我描述的那样构建你的项目,这个问题就不会发生,因为前向声明将是不必要的。

    【讨论】:

    • 当您定义在单个 .cpp 文件之外使用的模板时,这些定义应该对包含其标题的任何人可见。不会是最后一个(第 4 个)来自stackoverflow.com/a/495056/1937197 的 sn-p 与此一般规则相矛盾?
    • @MaxB:显式模板实例化是大多数人不关心的高级用例。你是对的,它是一般规则的一个例外......但是可以安全地忽略它,因为一旦移动到显式实例化模型,就会知道将所有内容移出标题(不是所有内容的一半,如OP)。
    猜你喜欢
    • 2019-11-12
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 2014-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多