C++23 多线程应用程序中的 std::start_lifetime_as 和 UB

mar*_*964 5 c++ object-lifetime undefined-behavior language-lawyer c++23

假设X和是适合这种用途的类型,那么在一个线程中的内存区域上Y使用作为一种类型并在另一个线程中的完全相同的内存上使用是否是UB?标准对此有什么规定吗?如果不正确,正确的解释是什么?std::start_lifetime_as<X>std::start_lifetime_as<Y>

Dav*_*ing 7

此类调用不会产生数据竞争,因为它们都不会访问任何内存位置,但因为(没有同步)两个线程都无法知道另一个线程尚未通过将其存储重用于另一个对象的对象来结束其所需对象的生命周期类型,创建的对象不能使用。(没有 \xe2\x80\x9c 一个线程可以使用它们的几率 \xe2\x80\x9d,因为它 \xe2\x80\x9c 最后 \xe2\x80\x9d:有一个执行,它没有\xe2\x80 \x99t,因此依赖它会产生未定义的行为。)

\n


duc*_*uck 2

对象生命周期实际上是标准中未明确规定的部分之一,特别是在并发性方面(在某些地方,措辞在我看来是完全有缺陷的),但我认为这个具体问题可以用现有的内容来回答。

首先,让我们消除数据竞争。

[种族简介]/21

如果程序的执行包含两个潜在并发冲突的操作,则该程序的执行包含数据竞争[...]

[介绍.races]/2 :

如果两个表达式求值之一修改内存位置,而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。

[内存简介]/3

内存位置可以是非位字段的标量类型的对象,也可以是全部具有非零宽度的相邻位字段的最大序列。

两个不相关的对象肯定不是相同的“内存位置”,因此 [intro.races]/21 不适用。

然而,[intro.object]/9说:

如果一个对象嵌套在另一个对象中,或者如果至少一个对象是零大小的子对象并且它们属于不同类型,则两个具有重叠生命周期(不是位域)的对象可能具有相同的地址;否则,它们具有不同的地址并占用不相交的存储字节。

这意味着在任何两个具有重叠存储的(不相关的)对象中,最多有一个对象可以在任何给定点的生命周期内。[basic.life]/1.5确保了这一点:

类型 T 的对象o的生命周期在以下情况结束: [...]

  • 对象占用的存储被释放,或者被未嵌套在o中的对象重用。

不允许在其生命周期之外访问(读取或写入)对象([basic.life]/4),并且我们刚刚确定了这一点X,并且Y不能同时处于生命周期内。因此,如果两个线程都继续访问创建的对象,则行为是未定义的:至少有一个线程将访问生命周期已结束的对象。