在类static static ODR中

时间:2015-09-27 20:46:03

标签: c++ gcc linker one-definition-rule

我对static成员的const类内初始化感到有些困惑。例如,在下面的代码中:

#include <iostream>

struct Foo
{
    const static int n = 42;
};

// const int Foo::n; // No ODR

void f(const int& param)
{
    std::cout << param << std::endl;
}

int g(const int& param)
{
    return param;
}

template<int N>
void h()
{
    std::cout << N << std::endl;
}

int main()
{
    // f(Foo::n); // linker error, both g++/clang++
    std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?!
    h<Foo::n>(); // this should be fine
}

Live example

我没有定义Foo::n(该行已注释)。所以,我希望调用f(Foo::n)在链接时失败,事实确实如此。但是,每当我使用std::cout << g(Foo::n) << std::endl;等优化标志时,以下行-O1/2/3仅通过gcc(clang仍会发出链接器错误)进行编译和链接。

  1. 为什么gcc(尝试使用gcc5.2.0和gcc 4.9.3)在打开优化时编译并链接代码?
  2. 我是否正确地说类内静态const成员的唯一用法是使用常量表达式,例如h<Foo::n>调用中的模板参数,在这种情况下,代码应链接?

4 个答案:

答案 0 :(得分:6)

我认为编译器在优化期间执行以下操作:

  • const static int n随处可见。没有为变量n分配内存,对它的引用变为无效。函数f()需要引用n,因此程序不会被编译。

  • 函数g简短明了。它有效地内联和优化。优化后,函数g不需要引用n,它只返回常量值42。

解决方案是在类外定义变量:

struct Foo
{
    const static int n;
};

const int Foo::n = 42;

答案 1 :(得分:4)

正式地,ODR违规是未定义的行为,因此编译器可能会展示它喜欢的任何行为。这就是行为随着优化级别和编译器而变化的原因 - 编译器没有义务维护特定的行为。

答案 2 :(得分:4)

ODR违规不需要诊断,来自草案C ++标准标准部分3.2 [basic.def.odr](强调我的前进):

  

每个程序都应包含每个非内联的一个定义   在该程序中使用的函数或变量; 没有诊断   需要

因此,在不同优化级别的不一致行为是完全一致的行为。

如果符合以下情况,则非正式变量为odr-used

  

其地址被采用,或者引用绑定到它,并且如果对其进行函数调用或者采用其地址,则使用函数。如果一个对象或函数使用了odr,它的定义必须存在于程序的某个地方;违反这一点的是链接时错误。

因此,fg都会使用并且需要定义。

关于odr-use的相关C ++ 14引用将来自 [basic.def.odr] 部分:

  

变量x的名称显示为可能评估的表达式ex odr-ex by ex除非应用   左值到右值的转换(4.1)到x产生一个不调用任何重要的常量表达式(5.19)   函数和如果x是一个对象,ex是表达式e的潜在结果集的一个元素,   其中左值到右值转换(4.1)应用于e,或e是丢弃值表达式 [...]

C ++ 11中的措辞相似,从C ++ 11到C ++ 14的更改反映在defect report 712中。

在C ++ 11之前它是a bit more complicated but in principle the same for this case

答案 3 :(得分:2)

根本没有定义。 GCC 4.9.2没有编译并用任何标志链接它。

请注意:

const static int n = 42;

声明初始值设定项,但不是定义