名前空間
変種
操作

std::launder

提供: cppreference.com
< cpp‎ | utility
 
 
 
ヘッダ <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 の表すアドレスに位置するオブジェクトを指すポインタを取得します。

形式的には、

  • ポインタ p がメモリ内のあるバイトのアドレス A を表している
  • オブジェクト X がアドレス A に位置している
  • X はその生存期間内である
  • X の型が T と同じである (すべてのレベルにおける cv 修飾を無視します)
  • 結果を通して到達可能であろうすべてのバイトが p を通して到達可能である (Yポインタ相互変換可能なオブジェクト Z の記憶域内または Z を直接の要素に持つ配列内のバイトは、オブジェクト Y を指すポインタを通して到達可能です)

場合、 std::launder(p) はオブジェクト X を指す T* 型の値を返します。 そうでなければ、動作は未定義です。

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])); 
// 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 <new>
#include <cstddef>
#include <cassert>
 
struct X {
  const int n; // note: X has a const member
  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};    // p does not point to new object because X::n is const; np does
  const int b = p->n; // undefined behavior
  const int c = p->m; // undefined behavior (even though m is non-const, p can't be used)
  const int d = std::launder(p)->n; // OK, std::launder(p) points to new object
  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; // 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
 
  A i;
  int n = i.transmogrify();
  // int m = i.transmogrify(); // undefined behavior
  int m = std::launder(&i)->transmogrify(); // OK
  assert(m + n == 3);
}