原子对象的值计算结果是否为未指定行为?C++标准与实例解析
C++原子操作加载结果的确定性分析
标准依据
根据C++标准[intro.races]第14款规定:
原子对象M的值(由求值B确定)应当是某个修改M的副作用A所存储的值,其中B不先于A发生。
这里的“某个修改M的副作用A”指修改顺序中任意一个可能的值,这一点可通过PR #6501确认。
代码实例
std::atomic<int> v{0}; auto c = [](){v.store(1,std::memory_order::release); return 1;}(); auto c2 = [](){v.store(2,std::memory_order::release); return 2;}(); int main(){ auto r = v.load(std::memory_order::acquire); // #1 }
问题核心
已知#1处的加载操作不会先于上述两个存储操作发生,那么#1处的值计算结果是修改顺序中的最新值,还是可以是0、1、2中的任意一个(即属于未指定行为)?当前编译器实现中结果始终为2,但该结果是否是确定的,还是仅为未指定行为的一种表现?
分析结论
#1处的结果属于未指定行为,理论上可读取到1或2,但绝不可能是0,具体原因如下:
- 全局变量的初始化(包括两个lambda的立即调用)都发生在
main函数执行之前,根据C++标准,这些初始化的副作用(v.store(1)和v.store(2))都happens beforemain中的加载操作#1,因此加载操作不可能读取到初始值0。 - C++标准并未强制规定这两个
release存储操作的执行顺序。实际编译器中结果始终为2,是因为初始化代码按源码顺序执行,v.store(2)在v.store(1)之后完成,修改顺序里2是最新值,但这只是编译器的实现选择,而非标准要求的必然结果。 - 依据[intro.races] p14的规则,加载操作只需读取任意一个不早于自身发生的存储副作用的值即可,这里两个存储操作都满足条件,因此标准允许读取1或2。编译器返回2只是未指定行为的一种常见表现,而非确定结果。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

