名稱空間
變體
操作

std::launder

來自 cppreference.com
< 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 可能導致指向非活動成員的指標
在常量表達式中可解引用
已禁止