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_typeがTと同じ型であること
適格要件
- 以下のいずれかの場合に、プログラムは不適格となる
Tがオブジェクト型でない- 配列型である
- (CV修飾された)
std::in_place_tである - (CV修飾された)
std::in_place_type_tの特殊化である
std::indirectの明示的特殊化・部分特殊化をユーザーが宣言した場合、動作は未定義である
メンバ関数
構築・破棄
| 名前 | 説明 | 対応バージョン |
|---|---|---|
(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
処理系
- Clang: 22 ❌
- GCC: 16.1 ✅
- Visual C++: 2026 Update 2 ❌
関連項目
std::polymorphic派生型を多態的に保持する値型std::unique_ptr所有権をもつが参照の意味論をもつスマートポインタstd::optional動的確保せずに無効値状態を表現する型
参照
- P3019R14
indirectandpolymorphic: Vocabulary Types for Composite Class Design- C++26で
indirectとpolymorphicが追加された
- C++26で