且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

静态const声明,变量的constexpr定义,有效的c ++?

更新时间:2022-12-24 15:19:10

正确的方法



在开始进行语言咨询之前,正确的方法是反过来进行。在头文件中:

The Right Way

Before we begin the language-lawyering, the correct approach is to do it the other way around. In the header file:

class Foo
{
     static constexpr int IntArray[] = { 1, 2, 3, 4 };
};

然后在源文件中:

constexpr int Foo::IntArray[];

如果声明了静态constexpr 类数据类定义中的成员,则必须在那里然后对其进行初始化。对于静态const 数据成员,这是可选的。如果您在程序中的任何地方使用 static constexpr 数据成员,则必须在完全没有一个初始化程序的一个源文件中提供与上述定义相同的定义。

If you declare a static constexpr class data member in the class definition, you must initialize it then and there. This is optional for static const data members. If you use the static constexpr data member anywhere in the program, you must give a definition like the one above, in exactly one source file, with no initializer.

问题中的示例代码风格不好,显然至少有一个编译器拒绝了它,但实际上似乎确实符合C ++ 14草案标准。 [ dcl / constexpr ]说:

The example code in the question is bad style, and apparently at least one compiler rejects it, but it does in fact seem to comply with the C++14 draft standard. [dcl/constexpr] says:


constexpr 说明符仅适用于变量或变量模板的定义,函数或函数模板的声明或文字类型的 static 数据成员的声明。如果函数,函数模板或变量模板的任何声明具有 constexpr 说明符,则其所有声明均应包含 constexpr 说明符。

The constexpr specifier shall be applied only to the definition of a variable or variable template, the declaration of a function or function template, or the declaration of a static data member of a literal type. If any declaration of a function, function template, or variable template has a constexpr specifier, then all its declarations shall contain the constexpr specifier.

通知的声明被遗漏都必须包含 constexpr 说明符。

Notice whose declarations are, by omission, not all required to contain the constexpr specifier.

稍后在同一部分:

对象声明中使用的

A constexpr 说明符将对象声明为 const 。这样的对象应具有文字类型并应进行初始化。 [...]

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. [...]

但也请参见[ class.static.data ]:


如果非-易挥发 const 静态数据成员是整数或枚举类型,其在类定义中的声明可以指定一个 brace-or-equal-initializer ,其中每个 initializer-clause 是一个 assignment-expression ,它是一个常量表达式。可以使用 constexpr 说明符在类定义中声明文字类型的静态数据成员;如果是这样,则其声明应指定一个
brace-or-equal-initializer ,其中每个 assignment-expression initializer-clause 常数表达式。 [注意:在这两种情况下,成员都可能出现在常量表达式中。 — —尾注]如果在程序中使用了该成员,则该成员仍应在命名空间范围内定义,并且该命名空间范围定义不应包含初始化程序。

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. 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 in the program and the namespace scope definition shall not contain an initializer.

在这种情况下, odr-used中的odr代表 one-definition-rule (单定义规则),表示其名称显示为可能评估的表达式。 ([basic.def.odr])最后一句意味着,如果您声明,则在类定义中 static constexpr int foo = 0; ,稍后您将使用它在表达式中,例如 int x = MyClass :: foo; ,则只有一个源文件需要具有 constexpr int MyClass这样的行:: foo; ,因此链接器知道将其放入哪个目标文件。

In this context, the odr in "odr-used" stands for the one-definition-rule and means "whose name appears as a potentially-evaluated expression." ([basic.def.odr]) The last sentence means that, if you declare, static constexpr int foo = 0; in the class definition, and you will later use it in an expression, such as int x = MyClass::foo;, then one and only one source file needs to have a line like constexpr int MyClass::foo; in it, so the linker knows which object file to put it in.