0-原子的初始化是否保证将值成员设置为0

Is 0-initialization of atomics guaranteed to set the value member to 0?

本文关键字:成员 设置 原子的 初始化 是否      更新时间:2023-10-16

std::atomic<integral_type>变量的0-初始化意味着什么?

问题的起源。我有一个std::atomic<std::int>的函数static std::array,我希望在第一次使用之前将其设置为0(不用说,数组所在的函数是以不可预测的方式从多个线程调用的)。

这段代码很好看,但由于原子是不可复制的,所以不能编译:

#include <array>
#include <atomic>
void foo() {
    using t = std::atomic<int>;
    static std::array<t, 2> arr = {0, 0}; // <-- explicit, but errors out (see below)
    static std::array<t, 2> arr2; // <-- implicit?, works
}

错误:使用已删除的函数"std::atomic::atoic(const std::原子&)"std::array arr={0,0};

现在,我知道静态std::array将0初始化它的所有成员,而std::atomic<>将0初始化。但是,我们是否有明确或隐含的保证,它实际上会将所有设置为0?常识说"是"——毕竟,我们假设类将有一个类型为int的成员,并且该成员将被0初始化。但这一假设是否建立在坚实的标准基础之上?

使用(通常是冗余的)大括号来避免复制初始化:

static t arr[2] = {{0}, {0}};
static std::array<t, 2> arr2 = {{{0}, {0}}}; /* Need extra pair here; otherwise {0} is
                                                treated as the initializer of the internal 
                                                array */

演示。当省略大括号时,我们正在进行复制初始化,这需要创建一个临时大括号并从中复制。使用大括号,我们可以进行复制列表初始化,其作用与直接列表初始化相同(即使用{0}初始化每个元素,这很好)。

您也可以等到引入了有保证的副本省略后再使用您的语法。

需要区分的是默认初始化和零初始化。不久前,我对这个主题很感兴趣,并得出结论,标准隐含地要求原子类在初始化时与structs的行为相同。

非原子基类型需要具有普通的可复制性,并且原子类型需要同时支持默认初始化和使用(或不使用)ATOMIC_VAR_INIT静态初始化。我没有任何干净的解决方案可以不使用内部结构或从结构派生(我为内部RTOS编写了一个原子实现)。

因此,如果没有要求的话,该标准至少会高度引导实现朝着零初始化完全符合您要求的解决方案发展。

我做了一个活生生的例子,比较了以下内容:

std::array<t, 2> default;
std::array<t, 2> zero{};
std::array<t, 2> explicit{{{0},{0}}};

您将看到零初始化与显式版本完全相同,并且使用gcc6.3.0更高效。

只是重申一下,我不认为标准明确要求零初始化才能实现这种行为,但据我所知,鉴于定义的内容,它必须如此。

从cppreference,std::atomic默认构造函数的文档说明:

构造新的原子变量。

1) 默认构造函数是琐碎的:除了静态和线程本地对象的零初始化之外,不进行任何初始化。std::atomic_init可以用来完成初始化。

所以你肯定需要Columbo的初始化循环。