名前空間
変種
操作

std::launder

提供: cppreference.com
< cpp‎ | utility
 
 
ユーティリティライブラリ
汎用ユーティリティ
日付と時間
関数オブジェクト
書式化ライブラリ (C++20)
(C++11)
関係演算子 (C++20で非推奨)
整数比較関数
(C++20)
スワップと型操作
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
一般的な語彙の型
(C++11)
(C++17)
(C++17)
(C++17)
(C++17)

初等文字列変換
(C++17)
(C++17)
 
ヘッダ <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] を通して到達可能でありません。

[編集]

#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);
}