C++类声明中类字段非常规初始化的标准及实现原理问询
偶然发现以下代码可以正常运行,之前没意识到这种写法是可行的:
#include <iostream> struct A { public: int a; //default constructor A(): a(-10){} A(int _a): a(_a){} static A test_1() { return A{1}; } static A test_2() { return A{10}; } };//class A class B { public: const A data; const int a = data.a; //How does this work? public: B() = default; B(const A& _data): data(_data) {} void printStatus() const { std::cout << "a = " << a << ";"<< std::endl; } };//class B int main() { B b_default; B b_1(A::test_1()); B b_2(A::test_2()); b_default.printStatus(); b_1.printStatus(); b_2.printStatus(); return 1; }
运行输出结果:
a = -10; a = 1; a = 10;
原本以为类声明中只能用编译期已知值初始化字段,现在有以下问题:
- 该特性是否在C++标准中有规定?
- 始于哪个C++版本?
- 内部实现机制是什么?
- 默认构造函数是否会在幕后添加赋值运算符?
问题解答
该特性是否在C++标准中有规定?
是的,这是C++标准明确规定的**非静态数据成员初始化器(Non-static data member initializers, NSDMI)**特性之一,允许使用类中其他已经完成初始化的非静态成员来初始化当前成员,只要遵循类成员的声明顺序即可。始于哪个C++版本?
这个特性从C++11开始引入,C++11之前确实只能用编译期常量或构造函数初始化列表来初始化非静态成员。内部实现机制是什么?
编译器会把非静态数据成员初始化器的逻辑合并到每个构造函数的初始化流程中(如果构造函数未显式初始化该成员),并且严格按照类中成员声明的顺序执行初始化,而非初始化器的书写顺序。
比如在类B中,data的声明早于a,所以不管a = data.a写在哪个位置,编译器都会先初始化data,再用data.a的值初始化a。对于默认构造函数B() = default;,编译器会自动生成初始化逻辑:先默认初始化data(调用A的默认构造函数),再用data.a初始化a;对于带参数的构造函数,先通过初始化列表初始化data为传入的对象,再用data.a初始化a。默认构造函数是否会在幕后添加赋值运算符?
不会。这里的操作是初始化阶段完成的,并非赋值。因为a是const类型,初始化完成后无法进行赋值操作。编译器是把成员初始化器的逻辑整合到构造函数的初始化列表中,属于对象初始化的一部分,全程没有赋值步骤。
内容的提问来源于stack exchange,提问作者one_two_three

