在 class 静态常量 ODR
In class static const ODR
const
成员的 static
in-class 初始化让我有点困惑。例如,在下面的代码中:
#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)
会在 link 时失败,而且确实如此。但是,每当我使用诸如 -O1/2/3
之类的优化标志时,以下行 std::cout << g(Foo::n) << std::endl;
编译并且 link 仅由 gcc 正常(clang 仍然发出 linker 错误)。
- 为什么gcc(试过gcc5.2.0和gcc 4.9.3)打开优化后编译和link代码?
- 我说 in-class static const 成员的唯一用法是常量表达式,例如 [=] 中的模板参数18=] 调用,在这种情况下代码应该 link?
根本没有定义。 GCC 4.9.2 无法编译并且 link 带有任何标志。
请注意:
const static int n = 42;
是声明和初始化程序,但不是定义。
我假设编译器在优化期间执行以下操作:
值 const static int n
在任何地方都是内联的。没有为变量 n
分配内存,对它的引用变得无效。函数 f()
需要对 n
的引用,因此程序未编译。
函数g
短小精悍。它被有效地内联和优化。优化后,函数g
不需要引用n
,它只是returns常量值42.
解决方法是在class外定义变量:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
形式上,ODR 违规是未定义的行为,因此编译器可以表现出它喜欢的任何行为。这就是为什么行为会随着优化级别和编译器而改变的原因——编译器没有义务维护特定的行为。
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.
因此,不同优化级别的不一致行为是完全一致的行为。
非正式地,变量是 odr-used 如果:
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. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
所以 f
和 g
都是 odr-uses 并且需要一个定义。
有关 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 [...]
C++11中的写法类似,从C++11到C++14的变化体现在defect report 712。
在 C++11 之前是 。
const
成员的 static
in-class 初始化让我有点困惑。例如,在下面的代码中:
#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)
会在 link 时失败,而且确实如此。但是,每当我使用诸如 -O1/2/3
之类的优化标志时,以下行 std::cout << g(Foo::n) << std::endl;
编译并且 link 仅由 gcc 正常(clang 仍然发出 linker 错误)。
- 为什么gcc(试过gcc5.2.0和gcc 4.9.3)打开优化后编译和link代码?
- 我说 in-class static const 成员的唯一用法是常量表达式,例如 [=] 中的模板参数18=] 调用,在这种情况下代码应该 link?
根本没有定义。 GCC 4.9.2 无法编译并且 link 带有任何标志。
请注意:
const static int n = 42;
是声明和初始化程序,但不是定义。
我假设编译器在优化期间执行以下操作:
值
const static int n
在任何地方都是内联的。没有为变量n
分配内存,对它的引用变得无效。函数f()
需要对n
的引用,因此程序未编译。函数
g
短小精悍。它被有效地内联和优化。优化后,函数g
不需要引用n
,它只是returns常量值42.
解决方法是在class外定义变量:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
形式上,ODR 违规是未定义的行为,因此编译器可以表现出它喜欢的任何行为。这就是为什么行为会随着优化级别和编译器而改变的原因——编译器没有义务维护特定的行为。
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.
因此,不同优化级别的不一致行为是完全一致的行为。
非正式地,变量是 odr-used 如果:
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. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
所以 f
和 g
都是 odr-uses 并且需要一个定义。
有关 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 [...]
C++11中的写法类似,从C++11到C++14的变化体现在defect report 712。
在 C++11 之前是