std::launder
From cppreference.com
| ヘッダ <new> で定義 |
||
| template< class T > constexpr T* launder( T* p ) noexcept; |
(C++17以降) | |
pに関するデバーチャライゼーションフェンス。オブジェクトが元の*pオブジェクトの最も派生したクラスとは異なる新しい基底クラスサブオブジェクトである可能性がある間、pが表すのと同じアドレスにあるオブジェクトへのポインタを返します。
形式的には、以下が与えられた場合:
- ポインタpはメモリ内のバイト
Aのアドレスを表す - オブジェクトxがアドレス
Aに位置する - xがそのライフタイム内にある
- xの型が、すべてのレベルでのcv-修飾子を無視して
Tと同じである - 結果を介して到達可能なすべてのバイトがpを介して到達可能である(ポインタがオブジェクトyを指す場合、それらのバイトがyとポインタ相互変換可能なオブジェクトzのストレージ内にあるか、またはzが要素である直近の包含配列内にある場合、それらのバイトはポインタを介して到達可能である)。
その場合、std::launder(p)はオブジェクトxを指す型T*の値を返します。それ以外の場合、動作は未定義です。
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は非アクティブなメンバーへのポインタを定数式で逆参照可能にする可能性がある |
禁止 |