值初始化是否适用于原子对象?

Does value initialization work for atomic objects?

本文关键字:对象 适用于 初始化 是否      更新时间:2023-10-16

通过这里的工作,我的意思是std::atomic<T> a{}有效地零初始化a。我一直这么想,直到现在才实际使用它。在解释我对此的理解之前,我想表明,至少,gcc 和 clang 在实践中正在这样做。

#include <cstring>
#include <atomic>
#include <iostream>
int main() {
using atomic = std::atomic<int>;  
auto p = (atomic*)operator new(sizeof(atomic));
std::memset(p, -1, sizeof(atomic));
new(p) atomic{};
std::cout << p->load() << std::endl;
}

输出在 gcc 和叮当声上都0

以下是我对为什么这应该起作用的解释(当然,您可能会不这么认为)。该标准说

在以下操作定义中:

  • a A 是指原子类型之一。

[...]

A::A() noexcept = default;

效果:使原子对象处于未初始化状态。[ 注意:这些语义可确保与 C 语言兼容。 — 尾注 ]

它基本上说默认构造函数是微不足道的,什么都不做。我对此没意见,但我看不出这如何使值初始化不适用。根据cppref,值初始化的影响包括(强调我的):

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

std::atomic有一个默认的默认构造函数,因此对象是

  1. 零初始化,然后
  2. 如果它具有非平凡的默认构造函数,则它是默认初始化的。

第 2 点在这里不适用,因为默认的默认构造函数微不足道,但我没有看到任何使第 1 点无效的语句。我的理解是正确的还是我错过了什么?

最终,值初始化情况的关键在于 [dcl.init]/7,项目符号 1 和 2:

对 T 类型的对象进行值初始化意味着:

  • 如果 T 是具有用户提供的构造函数 ([class.ctor]) 的(可能符合 cv 条件的)类类型(子句 [class]),则默认构造函数 对于 T 被调用(如果 T 没有,则初始化格式不正确 可访问的默认构造函数);
  • T 是一个(可能符合 cv 条件的)非联合类类型,没有用户提供的构造函数,则对象为零初始化,并且,如果 T 的隐式声明的默认构造函数是不平凡的,即 构造函数被调用。

应用上述两个项目符号中的哪一个取决于用户提供的 c'tor。我在另一个答案的评论中没有记住的是,当应用于该答案时,= default;的复杂性。如果我们看一下 [dcl.fct.def.default]/4 中给出的定义(强调我的):

显式默认函数和隐式声明函数是 统称为默认函数,实现应 为它们提供隐式定义([class.ctor] [class.dtor], [class.copy]),这可能意味着将它们定义为已删除。一个特别的 成员函数是用户提供的,如果它是用户声明的,而不是在其第一个声明中显式默认或删除。一个 用户提供的显式默认函数(即显式 默认在其第一个声明之后)定义在以下点 它是显式默认的;如果隐式定义了这样的函数 删除后,程序格式不正确。[ 注意:将函数声明为 在其第一个声明后默认可以提供有效的执行 和简洁的定义,同时启用稳定的二进制接口 不断发展的代码库。— 尾注 ]

我们看到atomic的默认 c'tor不是用户提供的,因为它被声明为默认值,而不是被声明然后定义为默认值。因此,[dcl.init]/7 的第二个项目符号是适用的,对象初始化为零,然后是(非)调用(微不足道的默认)构造函数,该构造函数不执行任何操作。