在类static static ODR中

vso*_*tco 8 c++ linker gcc one-definition-rule

staticconst成员的类内初始化感到有些困惑.例如,在下面的代码中:

#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
}
Run Code Online (Sandbox Code Playgroud)

Live example

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

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

And*_*nov 6

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

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

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

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

struct Foo
{
    const static int n;
};

const int Foo::n = 42;
Run Code Online (Sandbox Code Playgroud)


Sha*_*our 4

ODR 违规不需要诊断,来自 C++ 标准草案标准部分3.2[basic.def.odr](重点是我的未来):

每个程序都应包含该程序中使用的每个非内联函数或变量的确切定义;无需诊断

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

非正式地,如果满足以下条件,则变量被odr 使用

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

因此 和 都fg被 odr 使用并且需要定义。

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

名称显示为潜在求值表达式 ex 的变量 x 被ex odr 使用,除非将左值到右值转换 (4.1) 应用于 x 产生不调用任何非平凡函数的常量表达式 (5.19),并且如果x 是一个对象,ex 是表达式 e 的潜在结果集合中的一个元素,其中左值到右值转换 (4.1) 应用于 e,或者 e 是丢弃值表达式[...]

C++11中的措辞类似,从C++11到C++14的变化反映在缺陷报告712中。

在 C++11 之前,它有点复杂,但原则上对于这种情况是相同的