読者です 読者をやめる 読者になる 読者になる

C++1z 構造化束縛

C++

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::pairstd::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();

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。