命名空间
变体
操作

std::launder

来自 cppreference.cn
< cpp‎ | 工具
 
 
 
内存管理库
(仅作说明*)
未初始化内存算法
(C++17)
(C++17)
(C++17)
受约束的未初始化
内存算法
C 库

分配器
内存资源
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化存储
(直到 C++20*)
(直到 C++20*)
显式生命周期管理
 
 
定义于头文件 <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 可能导致指向非活动成员的指针
在常量表达式中可解引用
已禁止