最終更新日時: 2025年07月09日 00時33分39秒 (JST)
Akira Takahashi が更新

履歴 編集

# 参照メンバをもつクラスの置き換え [P0137R1](C++17)

このページはC++17に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

## 概要

`placement new`を使用して、参照型や`const`メンバ変数を含む構造体/クラスを置き換える際、オブジェクト生存期間(lifetime)に基づいた最適化の抑止をコンパイラに伝える関数`std::launder()`を用いることで、未定義動作を引き起こすような文脈で参照型や`const`メンバ変数へのアクセスができる。

## 仕様

n4659 [ptr.launder]/5より

    struct X { const int n; };
    X *p = new X{3};
    const int a = p->n;
    new (p) X{5};  // X::nはconstなので、pは新しいオブジェクトを指していない
    const int b = p->n;  // 未定義動作
    const int c = std::launder(p)->n;  // OK

## 例

    #include <iostream>
    #include <new>

    struct X
    {
      int& n;
    };

    int main()
    {
      int n = 12;
      X *p = new X{n};

      int m = 34;
      new (p) X{m};

      n = 56;
      m = 78;

      // const int a = p->n;  // 未定義動作
      const int a = std::launder(p)->n;  // OK

      std::cout << a << std::endl;
    }

### 出力

    78

## この機能が必要になった背景・経緯

以前は、`placement new`の戻り値を用いることで未定義動作を起こさないようにすることができた。そして、`std::optional`のようなクラスでは、次のように`placement new`の戻り値を保持するために、メンバにポインタを追加する必要があった。

    template <typename T>
    class coreoptional
    {
    private:
      T payload;
      T* p;  // placement newの戻り値を使えるようにする
    public:
      coreoptional(const T& t)
       : payload(t) {
         p = &payload;
      }
      template<typename... Args>
      void emplace(Args&&... args) {
        payload.~T();
        p = ::new (&payload) T(std::forward<Args>(args)...);
      }
      const T& operator*() const & {
        return *p;  // ここで payload を使わないでください!
      }
    };

このオーバーヘッドを避けるために`std::launder()`関数が導入された。

## 備考

ストレージの再利用については、ほかにも追加された機能がある。ストレージを再利用する際は、Lifetime - cppreference.comを参照してもよいと思われる。

## 参照

- std::launder関数 - yohhoyの日記
- ［C++］メンバに参照型を持つクラス（構造体）の取り扱い - 地面を見下ろす少年の足蹴にされる私
- P0532R0 On `launder()`
- Core Issue 1776: Replacement of class objects containing reference members
- std::launder - cppreference.com
- Pointer safety and placement new
- Implementability of std::optional （std :: optionalの実装可能性）
- Lifetime - cppreference.com