我对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
}
我没有定义Foo::n
(该行已注释)。所以,我希望调用f(Foo::n)
在链接时失败,事实确实如此。但是,每当我使用std::cout << g(Foo::n) << std::endl;
等优化标志时,以下行-O1/2/3
仅通过gcc(clang仍会发出链接器错误)进行编译和链接。
h<Foo::n>
调用中的模板参数,在这种情况下,代码应链接?答案 0 :(得分:6)
我认为编译器在优化期间执行以下操作:
值const static int n
随处可见。没有为变量n
分配内存,对它的引用变为无效。函数f()
需要引用n
,因此程序不会被编译。
函数g
简短明了。它有效地内联和优化。优化后,函数g
不需要引用n
,它只返回常量值42。
解决方案是在类外定义变量:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
答案 1 :(得分:4)
正式地,ODR违规是未定义的行为,因此编译器可能会展示它喜欢的任何行为。这就是行为随着优化级别和编译器而变化的原因 - 编译器没有义务维护特定的行为。
答案 2 :(得分:4)
ODR违规不需要诊断,来自草案C ++标准标准部分3.2
[basic.def.odr](强调我的前进):
每个程序都应包含每个非内联的一个定义 在该程序中使用的函数或变量; 没有诊断 需要强>
因此,在不同优化级别的不一致行为是完全一致的行为。
如果符合以下情况,则非正式变量为odr-used
其地址被采用,或者引用绑定到它,并且如果对其进行函数调用或者采用其地址,则使用函数。如果一个对象或函数使用了odr,它的定义必须存在于程序的某个地方;违反这一点的是链接时错误。
因此,f
和g
都会使用并且需要定义。
关于odr-use的相关C ++ 14引用将来自 [basic.def.odr] 部分:
变量x的名称显示为可能评估的表达式ex odr-ex by ex除非应用 左值到右值的转换(4.1)到x产生一个不调用任何重要的常量表达式(5.19) 函数和如果x是一个对象,ex是表达式e的潜在结果集的一个元素, 其中左值到右值转换(4.1)应用于e,或e是丢弃值表达式 [...]
C ++ 11中的措辞相似,从C ++ 11到C ++ 14的更改反映在defect report 712中。
在C ++ 11之前它是a bit more complicated but in principle the same for this case。
答案 3 :(得分:2)
根本没有定义。 GCC 4.9.2没有编译并用任何标志链接它。
请注意:
const static int n = 42;
是声明和初始值设定项,但不是定义。