考虑以下情况,我们在不同的翻译单元中有两个文件范围的对象,这是通过初始化顺序fiasco的未定义行为的常规设置:
a.hpp :
struct thing {
public:
thing(int value);
~thing();
int value() const;
static int count();
private:
int _value;
};
a.cpp :
#include "a.hpp"
#include <atomic>
namespace {
std::atomic<int> things;
}
thing::thing(int value) : _value(value) {
++things;
}
thing::~thing() {
--things;
}
int thing::value() const {
return _value;
}
int thing::count() {
return things.load();
}
b.cpp :
#include <iostream>
#include "a.hpp"
namespace {
thing static_thing(42);
}
void foo() {
std::cout << static_thing.value() << ' ' << thing::count() << '\n';
}
此代码是否受到 a.cpp 中文件作用域原子things
与 b.cpp <中作用域static_thing
之间的初始化顺序惨败的影响/ STRONG>?如果没有,为什么不呢?特别是,std :: atomic有什么特别的东西可以删除原本清楚的init命令惨败?是否有一个特定的概念可以命名为使用静态断言强制执行此操作?类似的东西:
static_assert(std::is_trivial<decltype(things)>::value, "file static counter is not trivial");
如果不是std::is_trivial
,是否有其他概念和相关类型特征可以更好地对此进行建模?
相反,是否有去初始化惨败?同样的问题,如果是,为什么或为什么不。
答案 0 :(得分:3)
std::atomic<>
是一种标准布局类型,具有普通的默认构造函数和简单的析构函数。因此,它在静态初始化阶段初始化,在动态初始化阶段之前,当全局对象的构造函数被调用时。
换句话说,这里没有发生初始化命令惨败。
由于您没有显式初始化命名空间作用域std::atomic<int>
,因此它将被初始化为零。
§3.6.2非局部变量的初始化
具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量应在任何其他初始化发生之前进行零初始化(8.5)。
一起,零初始化和常量初始化称为静态初始化;所有其他初始化是动态初始化。在进行任何动态初始化之前,应执行静态初始化。
答案 1 :(得分:1)
我对C ++“初始化顺序惨败”的理解是它仅适用于需要在运行时调用构造函数的情况。如果代码可以下载到内存位置初始化为固定值,那么该值将被放入“初始化数据”链接器部分(.data
),就像每个其他预先初始化的POD(Plain Ol'数据)一样),并没有惨败。
我建议atomic
符合这个标准。