尝试#2B:实现自己的std::once_flag
当量,与atomic<int>
(Live at Rextester):
my_type const& instantiate() {
static std::aligned_storage<sizeof(my_type), __alignof(my_type)>::type storage;
static std::atomic_int flag;
while (flag < 2) {
// all threads spin until the object is properly initialized
int expected = 0;
if (flag.compare_exchange_weak(expected, 1)) {
// only one thread succeeds at the compare_exchange.
try {
::new (&storage) my_type;
} catch(...) {
// Initialization failed. Let another thread try.
flag = 0;
throw;
}
// Success!
if (!std::is_trivially_destructible<my_type>::value) {
std::atexit([] {
reinterpret_cast<my_type&>(storage).~my_type();
});
}
flag = 2;
}
}
return reinterpret_cast<my_type&>(storage);
}
这仅依赖于编译器正确零初始化所有静态存储持续时间的对象,并且还使用了非标准扩展__alignof(<type>)
以正确对齐storage
,因为微软的编译团队不会被打扰添加没有两个下划线的关键字。
尝试#1:在具有
std::once_flag
(
Live demo at Coliru)结合使用
std::call_once
:
my_type const& instantiate() {
struct empty {};
union storage_t {
empty e;
my_type instance;
constexpr storage_t() : e{} {}
~storage_t() {}
};
static std::once_flag flag;
static storage_t storage;
std::call_once(flag, []{
::new (&storage.instance) my_type;
std::atexit([]{
storage.instance.~my_type();
});
});
return storage.instance;
}
为std::once_flag
默认的构造是constexpr
,所以它保证恒定初始化期间来构造。我觉得VC正确执行不断的初始化。编辑:不幸的是,通过VS12的MSVC仍然不支持constexpr
,所以这种技术有一些未定义的行为。我会再尝试。
C++ 11 [stmt.dcl/4不是无声:” ......这样的变量是 首次初始化控制穿过它的声明;这样的变量被认为是在 完成初始化如果初始化通过抛出异常退出,则初始化 未完成,因此在下一次控制进入声明时将再次尝试初始化。如果控制在初始化变量时并行输入 ,则并发执行应等待 完成初始化。“ – Casey