8

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.

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

4 回答 4

6

我想编译器在优化过程中会执行以下操作:

  • 该值const static int n在任何地方都内联。没有为变量分配内存n,对它的引用变得无效。该函数f()需要引用,n因此程序不会被编译。

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

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

struct Foo
{
    const static int n;
};

const int Foo::n = 42;
于 2015-09-27T21:05:23.507 回答
4

形式上,ODR 违规是未定义的行为,因此编译器可能会表现出它喜欢的任何行为。这就是行为随优化级别和编译器而变化的原因——编译器没有义务维护特定的行为。

于 2015-09-27T21:33:51.427 回答
4

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

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

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

在以下情况下,非正式地使用变量:

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

所以两者都是odr-usesf并且g需要一个定义。

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

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

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

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

于 2015-09-27T22:14:54.457 回答
2

根本没有定义。GCC 4.9.2 不编译并将其与任何标志链接。

注意:

const static int n = 42;

声明初始化器,但不是定义

于 2015-09-27T20:56:25.707 回答