命名空间
变体
操作

std::launder

来自 cppreference.com
< cpp‎ | utility
 
 
实用程序库
语言支持
类型支持 (基本类型,RTTI)
库功能测试宏 (C++20)
动态内存管理
程序实用程序
协程支持 (C++20)
可变参数函数
调试支持
(C++26)
三元比较
(C++20)
(C++20)(C++20)(C++20)
(C++20)(C++20)(C++20)
通用实用程序
日期和时间
函数对象
格式化库 (C++20)
(C++11)
关系运算符 (C++20 中已弃用)
整数比较函数
(C++20)(C++20)(C++20)   
(C++20)
交换类型操作
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
通用词汇类型
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
基本字符串转换
(C++17)
(C++17)

 
动态内存管理
未初始化内存算法
受限的未初始化内存算法
分配器
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)



 
 
定义在头文件 <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 可能会使指向非活动
成员的指针在常量表达式中可解引用
禁止