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クラスを使用する。
テンプレートパラメータ制約
適格要件
- 以下のいずれかの場合に、プログラムは不適格となる
std::indirectの明示的特殊化・部分特殊化をユーザーが宣言した場合、動作は未定義である
メンバ関数
構築・破棄
値へのアクセス
その他
メンバ型
| 名前 |
定義 |
対応バージョン |
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==からoperator!=が、operator<=>からoperator< / operator<= / operator> / operator>=が導出される。
推論補助
ハッシュサポート
| 名前 |
説明 |
対応バージョン |
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);
}
出力
バージョン
言語
処理系
関連項目
参照