[英]In class static const ODR
我对const
成员的static
类内初始化感到困惑。 例如,在下面的代码中:
#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
}
我没有定义Foo::n
(该行已注释)。 所以,我希望调用f(Foo::n)
在链接时失败,事实确实如此。 但是,以下行std::cout << g(Foo::n) << std::endl;
每当我使用优化标志(如-O1/2/3
)时,只能通过gcc编译和链接(clang仍会发出链接器错误)。
h<Foo::n>
调用中的模板参数,在这种情况下代码应链接? 我想编译器在优化期间执行以下操作:
值const static int n
无处不在。 没有为变量n
分配内存,对它的引用变为无效。 函数f()
需要引用n
因此不编译程序。
函数g
简短。 它有效地内联和优化。 在优化之后,函数g
不需要对n
的引用,它只返回常数值42。
解决方案是在类外定义变量:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
正式地,ODR违规是未定义的行为 ,因此编译器可能表现出它喜欢的任何行为。 这就是为什么行为随着优化级别和编译器而变化的原因 - 编译器没有义务维护特定的行为。
ODR违规不需要诊断,来自草案C ++标准标准第3.2
节[basic.def.odr]( 强调我的未来 ):
每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义; 无需诊断 。
因此,在不同优化级别的不一致行为是完全一致的行为。
非正式地一个变量ODR使用的 ,如果:
它的地址被采用,或者引用被绑定到它,并且如果对它进行函数调用或者采用它的地址,则函数是odr-used。 如果一个对象或函数使用了odr,它的定义必须存在于程序的某个地方; 违反这一点的是链接时错误。
所以f
和g
都是odr-uses并且需要定义。
关于odr-use的相关C ++ 14引用将来自[basic.def.odr]部分:
变量x的名称显示为潜在评估的表达式ex ,除非将lvalue-to-rvalue转换(4.1)应用于x, 否则将生成一个不调用任何非平凡函数的常量表达式(5.19),并且, 如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式 [...]
C ++ 11中的措辞类似,从C ++ 11到C ++ 14的变化反映在缺陷报告712中 。
在C ++ 11之前,它有点复杂,但在这种情况下原则上是相同的 。
根本没有定义。 GCC 4.9.2不编译并将其与任何标志链接。
注意:
const static int n = 42;
是声明和初始化程序 ,但不是定义 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.