union A{
int a;
struct{
int b;
} c;
};
c
和a
不是布局兼容类型,因此无法通过a
读取b
的值:
A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
请参见this question中的试验1和试验2
试验3
现在让我们使用std::launder
来实现它似乎不是用于的功能:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
p = std::launder(p); //(2')
*p+*p; //(3) UB?
你认为 std::launder
会改变什么吗?根据 [ptr.launder] 的说明:
加粗的句子强调了一个困扰我的问题。如果
template <class T> constexpr T* launder(T* p) noexcept;
要求:
p
表示内存中一个字节的地址。一个类型与T
相似且处于其生命周期内的对象 X 位于地址 A 处。通过结果可达的所有存储器字节均可通过p
可达 (见下文)。返回值: 类型为
T *
的指针,指向 X。备注: 只要其参数的值可以在核心常量表达式中使用,就可以调用此函数。如果指向对象 Y 的指针值可以访问一个存储字节,则该存储字节是可达的,其中 Y 是占用该存储字节的对象、与 Y 可以相互转换的对象或者如果 Y 是数组元素,则是直接封闭的数组对象。如果 T 是函数类型或 cv void,则程序不合法。
p
是无效的指针值,那么任何存储字节怎么可能是可访问的呢?另一方面,如果采用这种解释,std::launder
就无法使用。否则,在[basic.life]的“注”中所述,
p
在(2)处的值是否可以表示存储区域的指针值:
如果不满足这些条件,则可以通过调用
std::launder
([support.dynamic])从表示其存储地址的指针获取新对象的指针。