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

Lin*_*gxi 8 c++ atomic language-lawyer value-initialization c++11

通过在这里工作,我认为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;
}
Run Code Online (Sandbox Code Playgroud)

输出0gccclang上.

以下是我解释为什么这应该起作用(当然,你可能会这样想).标准说

在以下操作定义中:

  • A指的是原子类型之一.

[...]

A::A() noexcept = default;
Run Code Online (Sandbox Code Playgroud)

效果:使原子对象处于未初始化状态.[注意:这些语义确保与C的兼容性. - 结束语]

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

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

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

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

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

Sto*_*ica 6

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

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

  • 如果T是具有用户提供的构造函数([class.ctor])的(可能是cv限定的)类类型(Clause [class]),则调用T的默认构造函数(如果T,则初始化是错误的)没有可访问的默认构造函数);
  • T是一个(可能是cv限定的)非联合类类型,没有用户提供的构造函数,然后该对象被零初始化,如果T的隐式声明的默认构造函数是非平凡的,则调用该构造函数.
  • ...

应用上述两个子弹中的哪一个取决于用户提供的c'tor.在对其他答案的评论中我不记得的是,= default;当应用于此时的复杂性.如果我们看一下[dcl.fct.def.default]/4(强调我的)给出的定义:

显式默认函数和隐式声明函数统称为默认函数,实现应为它们提供隐式定义([class.ctor] [class.dtor],[class.copy]),这可能意味着将它们定义为已删除.如果特殊成员函数是用户声明的,并且在其第一个声明中未明确默认或删除,则由用户提供.用户提供的显式默认函数(即,在第一次声明后显式默认)是在明确默认的位置定义的; 如果将这样的函数隐式定义为已删除,则该程序格式错误.[注意:在第一次声明后将函数声明为默认值可以提供高效的执行和简洁的定义,同时为不断发展的代码库提供稳定的二进制接口. - 结束说明]

我们看到默认的c'tor atomic 不是用户提供的,因为它被声明为默认,而不是声明然后被定义为默认.所以[dcl.init]/7的第二个子弹是适用的,该对象是零初始化的,然后是(非)调用(普通的默认)构造函数,它什么都不做.