std::launder
来自 cppreference.cn
定义于头文件 <new> |
||
template< class T > constexpr T* launder( T* p ) noexcept; |
(C++17 起) | |
与 p 相关的去虚拟化屏障。返回一个指向对象的指针,该对象与 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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 2859 | C++17 | 可达的定义未考虑指针 来自指针可互相转换对象的算术 |
已包含 |
LWG 3495 | C++17 | std::launder 可能导致指向非活动成员的指针在常量表达式中可解引用 |
已禁止 |