[英]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
}
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仍会发出链接器错误)。
h<Foo::n>
call, in which case the code should link? 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. 所以
f
和g
都是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.