显式调用平凡默认构造为何产生额外赋值?NoInit构造优化疑问
咱们先把核心问题拆解清楚——你遇到的差异本质上是C++里**值初始化(Value Initialization)和默认初始化(Default Initialization)**的规则区别,再加上类成员in-class初始化器的行为影响,一步步来解释:
1. 为什么A(DefaultInit)和A(NoInit)表现不同?
先看struct A的两个构造函数:
A(DefaultInit) : a(), b(), c() {} // 生成赋值0的汇编 A(NoInit) {} // 无任何汇编代码
当你在成员初始化列表里写a()、c()这种形式时,触发的是值初始化,而非默认初始化:
- 对于
std::atomic<long>:它的默认构造函数是平凡的(啥也不做),但值初始化规则要求,对拥有平凡默认构造函数的类类型,会先零初始化对象,再调用默认构造函数(而后者不执行任何操作)。最终结果就是这些atomic成员被强制设为0,所以汇编里会出现赋值指令。 - 对于
int c:标量类型的值初始化就是零初始化,因此也会生成赋值0的代码。
而A(NoInit) {}没有显式初始化任何成员,触发的是默认初始化:
std::atomic<long>的默认初始化就是调用它的平凡构造函数,完全不做任何操作,自然无汇编代码。int c的默认初始化是不执行任何初始化,所以也不会生成相关汇编。
2. 为什么struct B的B(NoInit) : a(), b(), c()会生成赋值0的代码?
struct B的成员带有in-class初始化器(a{1}、b{2}、c{3}),这会改变构造函数的行为逻辑:
- 如果你在构造函数的成员初始化列表里没有提到某个成员,编译器会自动用in-class初始化器来初始化它(比如默认构造函数
B() {}就会把a设为1,b设为2,c设为3,符合你的预期)。 - 但当你显式写
a()、b()、c()时,你覆盖了in-class初始化器,同时触发了值初始化——和struct A的情况一致,atomic成员被零初始化,int也被零初始化,所以汇编里出现赋值0的指令。
3. 如何让B(NoInit)的行为和A(NoInit)完全一致?
你想要的是完全跳过所有初始化(包括in-class初始化器的执行),这时候需要用默认初始化的语法,同时覆盖in-class初始化器的默认行为。在C++11及以后,你可以用空的成员初始化器(只写成员名,不写括号或大括号)来触发默认初始化:
struct B { std::atomic<long> a{1}; std::atomic<long> b{2}; int c{3}; B() {} // 行为符合预期,使用in-class初始化器 B(NoInitCorrect) : a, b, c {} // 正确写法:触发默认初始化 };
这里的a, b, c就是告诉编译器:对这些成员执行默认初始化,既不使用值初始化,也不使用in-class初始化器。这样:
std::atomic<long>的默认初始化调用平凡构造函数,不做任何操作。int c的默认初始化不执行任何初始化。
最终生成的汇编就会和A(NoInit)完全一致,没有任何赋值指令。
需要注意的是,这种默认初始化后的成员值是未定义的(完全符合你想要的"NoInit"语义),标准允许编译器做任何处理,但实际场景中它们会保留内存里的原有垃圾值,完全满足你的需求。
内容的提问来源于stack exchange,提问作者themagicalyang
相关产品推荐
相关产品推荐

