ixS*_*Sci 4 c++ memory-fences c++11
鉴于此处的代码:
class lazy_init
{
mutable std::once_flag flag;
mutable std::unique_ptr<expensive_data> data;
void do_init() const
{
data.reset(new expensive_data);
}
public:
expensive_data const& get_data() const
{
std::call_once(flag,&lazy_init::do_init,this);
return *data;
}
};
Run Code Online (Sandbox Code Playgroud)
我在其他地方也看到了相同模式的一些变体.所以我的问题是:为什么这段代码被认为是保存?为什么编译器在调用std :: call_once之前不能只读取数据并最终得到不正确的数据?例如
tmp = data.get();
std::call_once(flag,&lazy_init::do_init,this);
return *tmp;
Run Code Online (Sandbox Code Playgroud)
我的意思是我没有找到任何可以阻止这种情况的障碍.
如果允许编译器生成与您描述的匹配的代码,那么用C++编程基本上是不可能的.
这在§1.9/ 14 程序执行(n3290)中说明:
在与要评估的下一个全表达式相关联的每个值计算和副作用之前,对与全表达式相关联的每个值计算和副作用进行排序.
您的return语句在前面的完整表达式之后排序.编译器必须输出代码,就好像在评估return语句之前已完全评估了前一个语句的所有副作用.
您的示例不遵守该规则,因为它*data在考虑std::call_once(...)完整表达式的副作用之前进行评估.
此外,std::call_once在其描述中有这一点(§30.4.4.2/ 2和3):
2/效果:不调用其func的call_once的执行是被动执行.调用其func的call_once的执行是一个活动执行.主动执行应该调用
INVOKE (DECAY_- COPY ( std::forward<Callable>(func)), DECAY_COPY (std::forward<Args>(args))...).如果对func的这种调用抛出异常,则执行异常,否则返回.异常执行应将异常传播给call_once的调用者.在任何给定的once_flag的call_once的所有执行中:最多一个应该是返回执行; 如果有返回执行,则应该是最后一次执行; 只有在返回执行时才会执行被动执行.[ 注意:被动执行允许其他线程可靠地观察先前返回执行产生的结果.- 结束说明 ]3/同步:对于任何给定的once_flag:所有活动执行都按总顺序发生; 活动执行的完成与此总订单中下一个的开始同步; 并且返回的执行与所有被动执行的返回同步.
因此标准要求同步以适合您的用例.