C++1zから、タプルやクラスを分解する「構造化束縛 (Structured Binding)」という機能が入ります。他の言語では多重代入のように呼ばれているもので、C++11標準ライブラリのstd::tie()
関数の代わりとして使用できます。
以下は、タプルを返す関数の戻り値を、分解して受け取るコードです:
tuple<int, string> f(); // aにはintの値、bにはstringの値が代入される auto [a, b] = f();
構造化束縛には、auto
プレースホルダーを型として指定したあと、角カッコ [ ]
内に、分解した値を受け取る変数名のリストを記述します。各変数に対して個別に具体的な型を指定することはできません。
タプルの場合は、std::tuple_size<T>::value
という式が妥当な型が対象となり、タプルのインタフェースget<I>(t)
とstd::tuple_element<I,T>::type
が使用できる型であれば、std::tuple
と同様に構造化束縛で分解できます(ADLで呼び出すため、get
にはstd::
が付いていない)。標準ライブラリでは、std::pair
とstd::array
もタプルとして分解できます。
これを使用して、map::insert()
の戻り値を簡単に受け取れたりしますね。
auto [iter, success] = m.insert(x); // iterは、挿入された要素を指すイテレータ // successは、挿入に成功したかを表すbool値 (重複時に失敗する)
クラスの分解
構造化束縛はタプルだけでなく、クラスも分解できます。クラスは、非静的publicメンバ変数が列挙されます(public基本クラスのものを含む)。そのクラスは、無名共用体メンバを持ってはいけません。
struct Point { int x, y; }; Point f(); // xにはPoint::xの値、yにはPoint::yの値が代入される const auto [x, y] = f();
メンバ変数は、ビットフィールドになっていても分解できます。
struct X { int flag : 1; int counter : 15; }; X f(); const auto [flag, counter] = f();
組み込み配列の分解
組み込み配列を分解することもできます。その際、受け取る変数名のリストは、配列の要素数と同じ個数だけ指定する必要があります。要素数が一致しない場合はコンパイルエラーになります:
// 配列の各要素を取得。 // 受け取る個数が配列の要素数と一致していなければならない int ar[] = {3, 1, 4}; auto [a, b, c] = ar;
参照修飾
変数を参照として受け取りたい場合は、auto
のあとに参照の修飾をします。以下は、範囲for文でstd::map
オブジェクトの要素をキーと値に分解するコードです:
std::map<int, string> m; for (const auto& [key, value] : m) {}
属性の指定
属性は、分解した個別の変数に対しては指定できず、全体に対して指定することになります:
[[maybe_unused]] auto [a, b] = f();
参照
- P0144R0 Structured Bindings
- P0144R1 Structured Bindings
- P0217R0 Proposed wording for structured bindings
- P0217R1 Proposed wording for structured bindings
- P0217R2 Proposed wording for structured bindings
- P0217R3 Proposed wording for structured bindings
お断り
この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。