C ++ 17引入了内联变量
C ++ 17解决了此问题,该问题适用于constexpr static
需要使用脱机定义的成员变量(如果使用过时)。有关C ++ 17之前的详细信息,请参见此答案的后半部分。
提案P0386内联变量引入了将说明inline
符应用于变量的功能。特别是这种情况constexpr
意味着inline
静态成员变量。提案说:
内联说明符可以应用于变量以及函数。内联声明的变量与内联声明的函数具有相同的语义:可以在多个翻译单元中相同地对其进行定义,必须在每个使用该翻译单元的翻译单元中对其进行定义,并且程序的行为就像只有一个变量。
并修改了[basic.def] p2:
声明是一个定义,除非
...
- 它在类定义之外声明了一个静态数据成员,并且该变量是使用constexpr说明符在类内定义的(不建议使用此用法;请参见[depr.static_constexpr]),
...
并添加[depr.static_constexpr]:
为了与以前的C ++国际标准兼容,可以在没有初始化程序的类外部冗余地声明constexpr静态数据成员。不建议使用此用法。[示例:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
constexpr int A::n; // redundant declaration (definition in C++ 2014)
—结束示例]
C ++ 14及更早版本
在C ++ 03中,只允许我们为const积分或const枚举类型提供类内初始化程序,而在C ++ 11中,使用constexpr
它扩展为文字类型。
在C ++ 11中,constexpr
如果未使用odr-used,则不需要为静态成员提供名称空间作用域定义,我们可以从C ++ 11标准草案9.4.2
[class.static.data]中看到这一点,即(重点是我的前进):
可以使用constexpr说明符在类定义中声明文字类型的静态数据成员;如果是这样,则其声明应指定大括号或相等的初始化程序,其中每个作为赋值表达式的初始化程序子句都是一个常量表达式。[注意:在这两种情况下,成员都可能出现在常量表达式中。— [end note]
如果在程序中使用了成员(3.2),则该成员仍应在命名空间范围中定义,并且命名空间范围定义不应包含初始化程序。
因此,问题就变成了这里baz
的用法:
std::string str(baz);
答案是肯定的,因此我们也需要命名空间范围定义。
那么我们如何确定变量是否被使用呢?3.2
[basic.def.odr]节中原始的C ++ 11措辞说:
除非表达式是未评估的操作数(第5条)或其子表达式,否则可能会对其进行评估。除非
其对象满足在常量表达式(5.19)中出现的要求并且立即应用左值到右值转换(4.1),否则将使用名称显示为可能评估的表达式的变量。
baz
确实会产生一个常数表达式,但是由于是数组,因此不适用于左值到右值转换baz
。4.1
[conv.lval]部分对此进行了介绍:
非函数,非数组类型T的glvalue(3.10)可以转换为prvalue.53 [...]
在数组到指针转换中应用了什么。
由于缺陷报告712,[basic.def.odr]的此措词已更改,因为此措词未涵盖某些情况,但这些更改不会更改此案例的结果。