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