在C++ 11之前,我们只能对整数或枚举类型的静态const成员执行类内初始化.Stroustrup在他的C++ FAQ中讨论了这个问题,给出了以下示例:
class Y {
const int c3 = 7; // error: not static
static int c4 = 7; // error: not const
static const float c5 = 7; // error: not integral
};
Run Code Online (Sandbox Code Playgroud)
以下推理:
那么为什么存在这些不方便的限制呢?类通常在头文件中声明,并且头文件通常包含在许多翻译单元中.但是,为避免复杂的链接器规则,C++要求每个对象都有唯一的定义.如果C++允许将需要作为对象存储在内存中的实体的类内定义,则该规则将被破坏.
但是,C++ 11放宽了这些限制,允许非静态成员的类内初始化(§12.6.2/ 8):
在非委托构造函数中,如果给定的非静态数据成员或基类未由mem-initializer-id指定(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer)然后,实体不是抽象类(10.4)的虚基类
- 如果实体是具有大括号或等于初始值的非静态数据成员,则按照8.5中的规定初始化该实体;
- 否则,如果实体是变体成员(9.5),则不执行初始化;
- 否则,实体默认初始化(8.5).
第9.4.2节还允许非const静态成员的类内初始化,如果它们用说明constexpr符标记的话.
那么我们在C++ 03中受到限制的原因究竟发生了什么?我们只是简单地接受"复杂的链接器规则"或者是否有其他改变使得这更容易实现?
一个类的完整类上下文是
(6.1)函数体,
(6.2)默认参数,
(6.3)noexcept-specifier([except.spec]),
(6.4)合同条件或
(6.5)默认成员初始化器在类的成员规范内。[?注意:如果嵌套类是在封闭类的成员规范内定义的,则嵌套类的完整类上下文也是任何封闭类的完整类上下文。-?尾注?]
上面突出显示的文本似乎为以下代码段提供了支持:
#include<iostream>
struct A{
int i = j + 1;
int j = 1;
};
int main(){
A a;
std::cout << a.i << '\n';
std::cout << a.j << '\n';
}
Run Code Online (Sandbox Code Playgroud)
,并且我希望它能打印出来
2
1
Run Code Online (Sandbox Code Playgroud)
无论GCC和铛打印
1
1
Run Code Online (Sandbox Code Playgroud)
但另外clang发出以下警告:
prog.cc:3:13: warning: field 'j' is uninitialized when used here [-Wuninitialized]
int i = j + 1;
^
prog.cc:8:7: note: in implicit default constructor for 'A' first required here
A …Run Code Online (Sandbox Code Playgroud)