显式调用普通默认构造函数会导致额外的赋值调用



>演示

首先是问题:我有一个带有默认构造函数的现有结构,该构造函数将其置于有效状态。我想要一个根本不进行初始化的 NoInit 构造函数。现有代码有一些成员变量的初始化期间声明语句,因此struct B示例,我尝试通过显式调用std::atomic的默认构造函数来覆盖初始化期间声明语句。

std::atomic默认构造函数不进行初始化:

默认构造函数很简单:不进行初始化 除了静态和线程本地对象的零初始化。

那么,为什么调用默认构造函数会导致程序集中的任何赋值呢?

struct A {
std::atomic<long> a;
std::atomic<long> b;
int c;
A() : a{1}, b{2}, c{3} {} // This is obviously fine.
A(DefaultInit) : a(), b(), c() {} // Should this not be similar to NoInit?
A(NoInit) {} // This is fine. Results in no code
};

第二个构造函数A(DefaultInit)导致对三个成员变量的赋值为 0,而A(NoInit)则根本不生成任何代码。

struct B {
std::atomic<long> a{1};
std::atomic<long> b{2};
int c{3};
B() {} // This is fine.
B(NoInit) : a(), b(), c() {} // Why setting to 0? Why should it generate any assignment code at all?
};

在第二个示例中,struct BB(NoInit)尝试显式调用std::atomic::atomic(),这应该不会导致store/mov指令。但这会导致所有三个变量的赋值为 0!

当然,标准确实说它是未定义的行为,如果是这样,为什么A(DefaultInit)A(NoInit)之间的区别。我希望B(NoInit)的组装与A(NoInit)相同。

另请注意,在这两种情况下,生成的程序集对于成员变量c没有区别。这与std::atomic无关

你不能真正"显式调用构造函数"。只能为对象指定初始值设定项,构造函数调用可以作为其中的一部分发生。我说是其中的一部分,因为一个无害的初始值设定项可以传达比人们想象的更多的意义。在这种情况下:

B(NoInit) : a(), b(), c() {} 

初始值设定项()意味着对每个成员进行值初始化。值初始化需要首先将其初始化为零(自 C++11 以来(。尽管何时以及发生初始化类型的确切情况和条件有些涉及,但您可以在链接的文章中阅读它们。

在您的特定情况下,对于std::atomic类型,会发生以下情况:

如果T是具有默认构造函数的类类型,则既不是 用户提供或删除的(也就是说,它可能是一个具有 隐式定义或默认的默认构造函数(,对象是 零初始化,如果它有一个 非平凡的默认构造函数;

而对于普通int,它是这样的:

否则,对象初始化为零。

这最终解释了你看到的所有三个零。


如果你想看不到零初始化,据我所知,你有两个选择。两者都是对初始值设定项规则的轻微利用:

  1. 省略初始值设定项,这是最简单的。
  2. 将其包装在值初始化调用用户提供的 c'tor 的类型中,该类型不执行任何操作(这里它正在工作,但要完全支持您的原始代码需要更多的样板valueless_initialization(。

最新更新