最終更新日時(UTC):
が更新

履歴 編集

class template
<memory>

std::indirect(C++26)

namespace std {
  template <class T, class Allocator = allocator<T>>
  class indirect;

  namespace pmr {
    template <class T>
    using indirect = std::indirect<T, polymorphic_allocator<T>>;
  }
}

概要

std::indirectクラスは、動的確保したオブジェクトに値の意味論をもたせる型である。

ポインタやスマートポインタは参照の意味論をもつため、クラスのメンバとして使うとコピーやconstの伝播が正しく行われず、クラスの特殊メンバ関数を正しく生成できないことがある。

std::indirectクラスは所有するオブジェクトを動的確保しつつ、以下のように値型として振る舞う:

  • ディープコピー: std::indirectオブジェクトをコピーすると、所有するオブジェクトTがそのコピーコンストラクタによって複製される
  • constの伝播: constなアクセス経路から所有オブジェクトにアクセスすると、所有オブジェクトにもconstが伝播する(operator*/operator->const版と非const版がある)
  • 不完全型のサポート: テンプレートパラメータT不完全型でもよい。これにより、再帰的なデータ構造を表現できる

これらの性質によって、std::indirectクラスのオブジェクトはクラスのメンバとして保持するのに適しており、コンパイラによる特殊メンバ関数の自動生成と協調して動作する。

とくに、pImplイディオム(実装クラスへのポインタをメンバとしてもち、実装詳細をヘッダから隠蔽してソースファイル側で行う手法)の実装に適している。実装クラスを不完全型のままstd::indirect型でメンバ変数として保持すると、生のポインタやstd::unique_ptrで実装した場合とは異なり、値のコピー(実装クラスのディープコピー)とconstの伝播がともに自動的に正しく行われる。

所有オブジェクトを持たない状態を「無効値状態 (valueless state)」と呼ぶ。std::indirectオブジェクトが無効値状態になるのは、ムーブ後に空となった場合のみである。無効値状態のオブジェクトに対するoperator*operator->の呼び出しは未定義動作を引き起こす。無効値状態かどうかはvalueless_after_move()メンバ関数で判定できる。

派生型のオブジェクトを多態的に保持したい場合は、std::polymorphicクラスを使用する。

テンプレートパラメータ制約

  • AllocatorはCpp17Allocator要件を満たすこと
  • std::allocator_traits<Allocator>::value_typeTと同じ型であること

適格要件

メンバ関数

構築・破棄

名前 説明 対応バージョン
(constructor) コンストラクタ C++26
(destructor) デストラクタ C++26
operator= 代入演算子 C++26

値へのアクセス

名前 説明 対応バージョン
operator* 所有するオブジェクトへの参照を取得する C++26
operator-> 所有するオブジェクトのメンバへアクセスする C++26
valueless_after_move 無効値状態かどうかを判定する C++26

その他

名前 説明 対応バージョン
get_allocator アロケータを取得する C++26
swap 他のindirectオブジェクトと値を交換する C++26

メンバ型

名前 定義 対応バージョン
value_type T C++26
allocator_type Allocator C++26
pointer allocator_traits<Allocator>::pointer C++26
const_pointer allocator_traits<Allocator>::const_pointer C++26

非メンバ(Hidden friends)関数

名前 説明 対応バージョン
swap 2つのindirectオブジェクトを交換する C++26

比較演算子

名前 説明 対応バージョン
operator== 等値比較を行う C++26
operator<=> 三方比較を行う C++26

operator==からoperator!=が、operator<=>からoperator< / operator<= / operator> / operator>=が導出される。

推論補助

名前 説明 対応バージョン
(deduction_guide) クラステンプレートの推論補助 C++26

ハッシュサポート

名前 説明 対応バージョン
hash hashクラスの特殊化 C++26

基本的な使い方

#include <cassert>
#include <memory>

int main()
{
  // 動的確保したintを、値の意味論で保持する
  std::indirect<int> a{42};
  assert(*a == 42);

  // コピーは所有オブジェクトのディープコピー
  std::indirect<int> b = a;
  *b = 10;
  assert(*a == 42); // aは影響を受けない
  assert(*b == 10);

  // 比較は所有オブジェクトの比較に転送される
  assert(a == std::indirect<int>{42});
  assert(b < a);
}

出力

pImplイディオムでの使用

実装クラスを不完全型のままstd::indirectで保持することで、値の意味論をもつpImplを簡潔に実装できる。コピー・ムーブ・デストラクタはコンパイラが生成し、constメンバ関数からは実装クラスにもconstが伝播する。

#include <cassert>
#include <memory>

// ヘッダ相当: 実装クラスImplは前方宣言のみ(不完全型)
class Widget {
  class Impl;
  std::indirect<Impl> impl_;
public:
  Widget(int x);
  int value() const;
  void set(int x);
  // コピー・ムーブ・デストラクタはコンパイラが生成する
};

// 実装相当: ここでImplが完全型になる
class Widget::Impl {
public:
  int v;
  Impl(int x) : v(x) {}
};

Widget::Widget(int x) : impl_{std::in_place, x} {}
int Widget::value() const { return impl_->v; } // constが伝播する
void Widget::set(int x) { impl_->v = x; }

int main()
{
  Widget a{42};
  assert(a.value() == 42);

  Widget b = a;            // 値のコピー(Implがディープコピーされる)
  b.set(10);
  assert(a.value() == 42); // aは影響を受けない
  assert(b.value() == 10);
}

出力

バージョン

言語

  • C++26

処理系

関連項目

参照