简体   繁体   English

在类static static ODR中

[英]In class static const ODR

I am a bit confused by the static in-class initialization of a const member. 我对const成员的static类内初始化感到困惑。 For example, in the code below: 例如,在下面的代码中:

#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 实例

I do not define Foo::n (the line is commented). 我没有定义Foo::n (该行已注释)。 So, I expect the call f(Foo::n) to fail at link time, and indeed it does. 所以,我希望调用f(Foo::n)在链接时失败,事实确实如此。 However, the following line std::cout << g(Foo::n) << std::endl; 但是,以下行std::cout << g(Foo::n) << std::endl; compiles and links fine only by gcc (clang still emits a linker error) whenever I use an optimization flag such as -O1/2/3 . 每当我使用优化标志(如-O1/2/3 )时,只能通过gcc编译和链接(clang仍会发出链接器错误)。

  1. Why does gcc (tried with gcc5.2.0 and gcc 4.9.3) compile and link the code when the optimization is turned on? 为什么gcc(尝试使用gcc5.2.0和gcc 4.9.3)在打开优化时编译并链接代码?
  2. And am I correct to say that the only usage of in-class static const members is in constant expressions , such as template parameters like in the h<Foo::n> call, in which case the code should link? 我是否正确地说, 类内静态const成员的唯一用法是在常量表达式中 ,例如h<Foo::n>调用中的模板参数,在这种情况下代码应链接?

I suppose that the compiler performs the following actions during the optimization: 我想编译器在优化期间执行以下操作:

  • The value const static int n is inlined everywhere. const static int n无处不在。 No memory is allocated for the variable n , references to it becomes invalid. 没有为变量n分配内存,对它的引用变为无效。 The function f() need a reference to n so the program is not compiled. 函数f()需要引用n因此不编译程序。

  • The function g is short and simple. 函数g简短。 It is effectively inlined and optimized. 它有效地内联和优化。 After the optimization, the function g does not need a reference to n , it just returns constant value 42. 在优化之后,函数g不需要对n的引用,它只返回常数值42。

The solution is to define the variable outside the class: 解决方案是在类外定义变量:

struct Foo
{
    const static int n;
};

const int Foo::n = 42;

Formally, ODR violations are undefined behaviour , so the compiler may exhibit any behaviour it likes. 正式地,ODR违规是未定义的行为 ,因此编译器可能表现出它喜欢的任何行为。 That's why the behaviour changes with optimization level and compiler- the compiler has no obligation to maintain a particular behaviour. 这就是为什么行为随着优化级别和编译器而变化的原因 - 编译器没有义务维护特定的行为。

ODR violations do not require a diagnostic, from the draft C++ standard standard section 3.2 [basic.def.odr] ( emphasis mine going forward ): ODR违规不需要诊断,来自草案C ++标准标准第3.2节[basic.def.odr]( 强调我的未来 ):

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; 每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义; no diagnostic required . 无需诊断

So inconsistent behavior at different optimization levels is perfectly conformant behavior. 因此,在不同优化级别的不一致行为是完全一致的行为。

Informally a variable is odr-used if: 非正式地一个变量ODR使用的 ,如果:

its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. 它的地址被采用,或者引用被绑定到它,并且如果对它进行函数调用或者采用它的地址,则函数是odr-used。 If an object or a function is odr-used, its definition must exist somewhere in the program; 如果一个对象或函数使用了odr,它的定义必须存在于程序的某个地方; a violation of that is a link-time error. 违反这一点的是链接时错误。

So both f and g will be odr-uses and require a definition. 所以fg都是odr-uses并且需要定义。

The relevant C++14 quote on odr-use would be from section [basic.def.odr] : 关于odr-use的相关C ++ 14引用将来自[basic.def.odr]部分:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression [...] 变量x的名称显示为潜在评估的表达式ex ,除非将lvalue-to-rvalue转换(4.1)应用于x, 否则将生成一个不调用任何非平凡函数的常量表达式(5.19),并且, 如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式 [...]

The wording in C++11 is similar, the changes from C++11 to C++14 are reflected in defect report 712 . C ++ 11中的措辞类似,从C ++ 11到C ++ 14的变化反映在缺陷报告712中

Before C++11 it is a bit more complicated but in principle the same for this case . 在C ++ 11之前,它有点复杂,但在这种情况下原则上是相同的

There is no definition at all. 根本没有定义。 GCC 4.9.2 doesn't compile and link that with any flags. GCC 4.9.2不编译并将其与任何标志链接。

Note, that: 注意:

const static int n = 42;

is a declaration and initializer , but not a definition . 声明初始化程序 ,但不是定义

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM