为什么在类主体中无法初始化静态常量字符串成员

Vla*_*iev 3 c++ c++14

我使用的是 c++ 14。我需要在我的类中使用静态常量字符串。但是当我写

class myClass
{
     static constexpr const std::string S="aa";
}
Run Code Online (Sandbox Code Playgroud)

它不编译。编译器(g++)的结果是

class myClass
{
     static constexpr const std::string S="aa";
}
Run Code Online (Sandbox Code Playgroud)

如果我用字符指针写它,如:

class myClass
{
     static constexpr const *char S="aa";
}
Run Code Online (Sandbox Code Playgroud)

它被编译。

我知道字符串可以在课外初始化。我的问题是为什么不编译第一个变体,第二个变体。我想知道标准怎么说。

Ser*_*sta 7

char是一个基本类型(参考 3.9.1 Fundamental types [basic.fundamental] in Draft n4296 for C++14)。它既没有构造函数也没有析构函数,可以在constexpr.

另一方面,std::string是类类型。更确切地说,它是 的专业化basic_string,即basic_string<char>。并且basic_string该类有很多构造函数,包括带有const char *在您的代码中使用的参数的构造函数,但没有声明constexpr,因此您无法构建constexpr std::string. 这是有道理的,因为 a 的创建std::string绝非易事,并且包括 char 数组的动态分配:

basic_string(const charT* s, const Allocator& a = Allocator());
Run Code Online (Sandbox Code Playgroud)

... 数组的已分配副本,其第一个元素由 s 指向

但恕我直言,正确的方法是声明一个数组:

class myClass
{
     static constexpr const char S[] ="aa";
}
const char A::S[];  // do not forget to define the static element if it is later odr-used...
Run Code Online (Sandbox Code Playgroud)

顺便说一下,如果以后在程序中使用静态数据成员,则应在其类定义之外定义静态数据成员。实现通常允许在类定义之外定义整数常量或指针(无论如何,一个定义规则不需要诊断,未定义的行为允许预期结果),但严格的 C++ 语义不允许。

参考 C++14 的 n4296 草案:9.4.2 静态数据成员 [class.static.data] §3

如果非易失性 const 静态数据成员是整数或枚举类型,则其在类定义中的声明可以指定一个大括号或等号初始值设定项,其中每个作为赋值表达式的初始值设定项子句都是一个常量表达式 (5.20)。可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员;如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。[ 注意:在这两种情况下,成员都可能出现在常量表达式中。—尾注] 如果该成员在程序中被 odr-used (3.2) 使用,则该成员仍应在命名空间范围内定义,并且命名空间范围定义不应包含初始值设定项。