c++ 17引入了内联变量
c++ 17修复了这个问题,如果使用奇数,则constexpr静态成员变量需要出行定义。关于c++ 17之前的细节,请参阅这个答案的后半部分。
提案P0386内联变量引入了将内联说明符应用于变量的能力。特别是在这种情况下,constexpr意味着静态成员变量的内联。提案说:
内联说明符可以应用于变量,也可以应用于函数。声明的变量
Inline与声明为Inline的函数具有相同的语义:它可以定义为in
必须在使用odr的每个翻译单元中定义多个翻译单元
程序的行为就像只有一个变量一样。
并修改[basic.def]p2:
声明就是定义,除非
...
它在类定义之外声明一个静态数据成员,并且变量是在类中使用constexpr说明符定义的(这种用法已弃用;参见[depr.static_constexpr]),
...
添加[depr.static_constexpr]:
为了与以前的c++国际标准兼容,使用了一个constexpr
静态数据成员可以在类外部冗余地重新声明
没有初始化式。不建议使用这种用法。(例子:
结构A {
Static constexpr int n = 5;//定义(c++ 2014中的声明)
};
A::n;//冗余声明(c++ 2014定义)
- end示例]
c++ 14及更早的版本
在c++ 03中,我们只允许为const积分或const枚举类型提供类内初始化式,在c++ 11中使用constexpr将其扩展为文字类型。
在c++ 11中,如果静态constexpr成员不是odr使用的,我们不需要为它提供命名空间作用域定义,我们可以从c++ 11标准草案第9.4.2节[class.static. static]中看到这一点。它说(强调我的未来):
[...]A static data member of literal type can be declared in the class
definition with the constexpr specifier; if so, its declaration shall
specify a brace-or-equal-initializer in which every initializer-clause
that is an assignment-expression is a constant expression. [ Note: In
both these cases, the member may appear in constant expressions. —end
note ]
The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition
shall not contain an initializer.
那么问题来了,这里用的是baz奇数吗?
std::string str(baz);
答案是肯定的,因此我们还需要一个名称空间作用域定义。
那么我们如何确定一个变量是否是奇数使用的呢?c++ 11第3.2节[basic.def.odr]中的原始措辞是这样的:
除非表达式为未求值表达式,否则它是潜在求值表达式
操作数(第5条)或其子表达式。变量的名称
作为一个潜在求值表达式出现是奇数使用除非
对象是满足出现在对象中的要求的对象
常量表达式(5.19)和左值到右值的转换
(4.1)立即生效。
因此,baz确实产生了一个常量表达式,但左值到右值的转换不会立即应用,因为它不适用,因为baz是一个数组。这在第4.1节[convl .lval]中提到:
非函数、非数组类型T的glvalue(3.10)可以为
转换为右值。53[…]
在数组到指针的转换中应用了什么。
由于缺陷报告712,更改了[basic.def.odr]的措辞,因为有些情况没有包含在这个措辞中,但这些更改不会改变这个情况的结果。