std::launder
来自 cppreference.com
定义在头文件 <new> 中 |
||
template< class T > constexpr T* launder( T* p ) noexcept; |
(自 C++17 起) | |
关于 p 的去虚拟化栅栏。返回指向与 p 指向相同内存的指针,但假定被引用对象具有不同的生命周期和动态类型。
正式地说,给定
- 指针 p 代表内存中一个字节的地址
A
- 一个对象 x 位于地址
A
- x 在其 生命周期 内
- x 的类型与
T
相同,忽略所有级别的 cv 限定符 - 每个可以通过结果访问的字节都可以通过 p 访问(字节可以通过指向对象 y 的指针访问,如果这些字节在与 y 指针可互换 的对象 z 的存储范围内,或者在 z 是其元素的紧邻封闭数组中)。
那么 std::launder(p) 返回一个类型为 T*
的值,该值指向对象 x。否则,行为未定义。
如果 T
是函数类型或(可能带有 cv 限定符的)void,则程序格式错误。
如果且仅当其参数的(转换)值可以代替函数调用使用时,std::launder
才能在 核心常量表达式 中使用。换句话说,std::launder
不会放宽常量评估中的限制。
[编辑] 注意
std::launder
对其参数没有影响。必须使用其返回值访问对象。因此,丢弃返回值始终是错误的。
std::launder
的典型用途包括
- 获取指向在现有相同类型对象的存储空间中创建的对象的指针,其中指向旧对象的指针不能 重复使用(例如,因为两个对象都是基类子对象);
- 获取指向通过放置
new
从指向为该对象提供存储空间的对象的指针创建的对象的指针。
可达性 限制确保 std::launder
不能用于访问无法通过原始指针访问的字节,从而干扰编译器的逃逸分析。
int x[10]; auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); // OK int x2[2][10]; auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0])); // Undefined behavior: x2[1] would be reachable through the resulting pointer to x2[0] // but is not reachable from the source struct X { int a[10]; } x3, x4[2]; // standard layout; assume no padding auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); // Undefined behavior: x4[1] would be reachable through the resulting pointer to x4[0].a // (which is pointer-interconvertible with x4[0]) but is not reachable from the source struct Y { int a[10]; double y; } x5; auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); // Undefined behavior: x5.y would be reachable through the resulting pointer to x5.a // but is not reachable from the source
[编辑] 示例
运行此代码
#include <cassert> #include <cstddef> #include <new> struct Base { virtual int transmogrify(); }; struct Derived : Base { int transmogrify() override { new(this) Base; return 2; } }; int Base::transmogrify() { new(this) Derived; return 1; } static_assert(sizeof(Derived) == sizeof(Base)); int main() { // Case 1: the new object failed to be transparently replaceable because // it is a base subobject but the old object is a complete object. Base base; int n = base.transmogrify(); // int m = base.transmogrify(); // undefined behavior int m = std::launder(&base)->transmogrify(); // OK assert(m + n == 3); // Case 2: access to a new object whose storage is provided // by a byte array through a pointer to the array. struct Y { int z; }; alignas(Y) std::byte s[sizeof(Y)]; Y* q = new(&s) Y{2}; const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined // behavior: reinterpret_cast<Y*>(&s) // has value "pointer to s" and does // not point to a Y object const int g = q->z; // OK const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK [](...){}(f, g, h); // evokes [[maybe_unused]] effect }
[编辑] 缺陷报告
以下更改行为的缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确的行为 |
---|---|---|---|
LWG 2859 | C++17 | 可达性 的定义未考虑指针 来自指针可互换对象的算术运算 |
包含 |
LWG 3495 | C++17 | std::launder 可能会使指向非活动成员的指针在常量表达式中可解引用 |
禁止 |