std::launder
提供: cppreference.com
ヘッダ <new> で定義
|
||
template <class T> constexpr T* launder(T* p) noexcept; |
(C++17以上) (C++20未満) |
|
template <class T> [[nodiscard]] constexpr T* launder(T* p) noexcept; |
(C++20以上) | |
p
の表すアドレスに位置するオブジェクトを指すポインタを取得します。
形式的には、以下の内容を満たす場合、 std::launder(p)
はオブジェクト X
を指す T*
型の値を返します。 そうでなければ、動作は未定義です。
- ポインタ
p
がメモリ内のあるバイトのアドレスA
を表している。 - オブジェクト
X
がアドレスA
に位置している。 -
X
はその生存期間内である。 -
X
の型がT
と同じである (すべてのレベルにおける cv 修飾を無視します)。 - 結果を通して到達可能であろうすべてのバイトが p を通して到達可能である (
Y
とポインタ相互変換可能なオブジェクトZ
の記憶域内またはZ
を直接の要素に持つ配列���のバイトは、オブジェクトY
を指すポインタを通して到達可能です)。
T
が関数型または void
(または cv 修飾された void
) の場合、プログラムは ill-formed です。
引数の値がコア定数式で使用できるならば、 std::launder
はコア定数式で使用できます。
[編集] ノート
std::launder
は引数に対する効果は持ちません。 オブジェクトにアクセスするためにはその戻り値を使用しなければなりません。 そのため戻り値を破棄することは常に誤りです。
一般的な std::launder
の用途には以下のようなものがあります。
- オブジェクトが
const
または参照のデータメンバを持つために、またはいずれかのオブジェクトが基底クラス部分オブジェクトであるために、古いオブジェクトを指すポインタを再利用できない場合に、同じ型の既存のオブジェクトの記憶域に作成されたオブジェクトを指すポインタを取得する。 - 記憶域を提供するオブジェクトを指すポインタから配置
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])); // 未定義動作。 // 結果のポインタ p2 (これは &x2[0] を指します) を通して到達可能な x2[1] に // 元のポインタ &x2[0][0] を通して到達可能でありません。 struct X { int a[10]; } x3, x4[2]; // 標準レイアウト (パディングがない想定) auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK。 auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0])); // 未定義動作。 // 結果のポインタ p4 (これは x4[0].a を指します (これは x4[0] とポインタ相互変換可能です)) // を通して到達可能な x4[1] に元のポインタ &x4[0].a[0] を通して到達可能でありません。 struct Y { int a[10]; double y; } x5; auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0])); // 未定義動作。 // 結果のポインタ p5 (これは x5.a を指します (これは x5 とポインタ相互変換可能です)) // を通して到達可能な x5.y に元のポインタ &x5.a[0] を通して到達可能でありません。
[編集] 例
Run this code
#include <new> #include <cstddef> #include <cassert> struct X { const int n; // 注: X は const メンバを持ちます。 int m; }; struct Y { int z; }; struct A { virtual int transmogrify(); }; struct B : A { int transmogrify() override { new(this) A; return 2; } }; int A::transmogrify() { new(this) B; return 1; } static_assert(sizeof(B) == sizeof(A)); int main() { X *p = new X{3, 4}; const int a = p->n; X* np = new (p) X{5, 6}; // X::n が const のため、 p は新しいオブジェクトを指しません // (np は新しいオブジェクトを指します)。 const int b = p->n; // 未定義動作。 const int c = p->m; // 未定義動作 (たとえ m が非 const でも、 p は使用できません)。 const int d = std::launder(p)->n; // OK、 std::launder(p) は新しいオブジェクトを指します。 const int e = np->n; // OK。 alignas(Y) std::byte s[sizeof(Y)]; Y* q = new(&s) Y{2}; const int f = reinterpret_cast<Y*>(&s)->z; // 未定義動作。 // reinterpret_cast<Y*>(&s) は // 「s へのポインタ」値を持ちますが // Y オブジェクトを指しはしません。 const int g = q->z; // OK。 const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK。 A i; int n = i.transmogrify(); // int m = i.transmogrify(); // 未定義動作。 int m = std::launder(&i)->transmogrify(); // OK。 assert(m + n == 3); }