名前空間
変種
操作

構造化束縛宣言 (C++17以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

指定された名前を初期化子のサブオブジェクトまたは要素に束縛します。

参照と同様に、構造化束縛は既存のオブジェクトへのエイリアスです。参照とは異なり、構造化束縛は参照型である必要はありません。

attr (省略可能) decl-specifier-seq ref-qualifier (省略可能) [ sb-identifier-list ] initializer ;
attr - 任意の数の属性のシーケンス
decl-specifier-seq (宣言指定子シーケンス) - 以下の指定子のシーケンス (単純宣言の規則に従います)
(C++26以降)
参照修飾子 - & または && のいずれか
sb-identifier-list - この宣言によって導入されるコンマ区切りの識別子のリスト。各識別子には属性指定子シーケンスが続く場合があります(C++26以降)
initializer - 初期化子 (下記参照)


initializerは以下のいずれかです

= expression (1)
{ expression } (2)
( expression ) (3)
- 任意の式 (括弧で囲まれていないコンマ式を除く)


構造化束縛宣言は、sb-identifier-list内のすべての識別子を囲むスコープ内の名前として導入し、それらをexpressionによって示されるオブジェクトのサブオブジェクトまたは要素に束縛します。このように導入された束縛は構造化束縛と呼ばれます。

sb-identifier-list内の識別子の1つは、省略記号が先行する場合があります。そのような識別子は構造化束縛パックを導入します。

識別子はテンプレート化されたエンティティを宣言する必要があります。

(C++26以降)

構造化束縛とは、sb-identifier-list内の識別子のうち、省略記号が先行しないもの、または同じ識別子リストで導入された構造化束縛パックの要素です(C++26以降)

目次

[編集] 束縛プロセス

構造化束縛宣言は、最初に初期化子の値を保持するためのユニークな名前の変数 (ここではeで表されます) を次のように導入します。

  • expressionが配列型cv1 Aであり、ref-qualifierが存在しない場合、eattr (省略可能) specifiers A e;として定義します。ここで、specifiersdecl-specifier-seq内の指定子からautoを除いたシーケンスです。
次に、eの各要素は、initializerの形式で指定されたように、expressionの対応する要素から初期化されます。
  • それ以外の場合、eattr (省略可能) decl-specifier-seq ref-qualifier (省略可能) e initializer ;として定義します。

eの識別子式の型 (すなわち、Estd::remove_reference_t<decltype((e))>と同等) をEと表記します。

E構造化束縛サイズとは、構造化束縛宣言によって導入する必要がある構造化束縛の数です。

sb-identifier-list内の識別子の数は、Eの構造化束縛サイズと等しくなければなりません。

(C++26まで)

sb-identifier-list内の識別子の数をNEの構造化束縛サイズをSとします。

  • 構造化束縛パックがない場合、NSと等しくなければなりません。
  • それ以外の場合、非パック要素の数 (すなわち、N - 1) はS以下でなければならず、構造化束縛パックの要素の数はS - N + 1です (これはゼロでも構いません)。
(C++26以降)
struct C { int x, y, z; };
 
template<class T>
void now_i_know_my() 
{
    auto [a, b, c] = C(); // OK: a, b, c refer to x, y, z, respectively
    auto [d, ...e] = C(); // OK: d refers to x; ...e refers to y and z
    auto [...f, g] = C(); // OK: ...f refers x and y; g refers to z
    auto [h, i, j, ...k] = C();    // OK: the pack k is empty
    auto [l, m, n, o, ...p] = C(); // error: structured binding size is too small
}

構造化束縛宣言は、Eに応じて3つの可能な方法のいずれかで束縛を実行します。

  • ケース1: Eが配列型の場合、名前は配列要素に束縛されます。
  • ケース2: Eが非共用体クラス型で、std::tuple_size<E>valueという名前のメンバーを持つ完全型である場合 (そのようなメンバーの型やアクセス可能性に関係なく)、"タプルライクな"束縛プロトコルが使用されます。
  • ケース3: Eが非共用体クラス型だが、std::tuple_size<E>が完全型でない場合、名前はEのアクセス可能なデータメンバーに束縛されます。

これら3つのケースは、以下で詳細に説明されています。

各構造化束縛には、以下の説明で定義される参照型があります。この型は、括弧で囲まれていない構造化束縛にdecltypeを適用したときに返される型です。

[編集] ケース1: 配列の束縛

sb-identifier-list内の各構造化束縛は、配列の対応する要素を参照する左辺値の名前になります。Eの構造化束縛サイズは配列要素の数に等しくなります。

各構造化束縛の参照型は配列要素型です。配列型Eがcv修飾されている場合、その要素型もcv修飾されることに注意してください。

int a[2] = {1, 2};
 
auto [x, y] = a;    // creates e[2], copies a into e,
                    // then x refers to e[0], y refers to e[1]
auto& [xr, yr] = a; // xr refers to a[0], yr refers to a[1]

[編集] ケース2: タプル操作を実装する型の束縛

std::tuple_size<E>::valueは、整形式の整数定数式である必要があり、Eの構造化束縛サイズはstd::tuple_size<E>::valueと等しくなります。

各構造化束縛に対して、型が「std::tuple_element<I, E>::typeへの参照」である変数が導入されます。対応する初期化子が左辺値であれば左辺値参照、そうでなければ右辺値参照です。I番目の変数の初期化子は次のとおりです。

  • Eのスコープ内でクラスメンバーアクセスルックアップによって識別子getを検索した結果、最初のテンプレートパラメータが非型パラメータである関数テンプレートである宣言が少なくとも1つ見つかった場合、e.get<I>()
  • それ以外の場合、get<I>(e)。ここで、getは、非ADLルックアップを無視して、引数依存ルックアップによってのみ検索されます。

これらの初期化子式では、エンティティeの型が左辺値参照である場合 (これはref-qualifier&であるか、&&であり初期化子式が左辺値である場合にのみ発生します) 、eは左辺値であり、それ以外の場合はx値です (これにより一種の完全転送が効果的に実行されます)。Istd::size_tのprvalueであり、<I>は常にテンプレートパラメータリストとして解釈されます。

変数はeと同じ記憶域期間を持ちます。

構造化束縛は、その変数に束縛されたオブジェクトを参照する左辺値の名前になります。

I番目の構造化束縛の参照型std::tuple_element<I, E>::typeです。

float x{};
char  y{};
int   z{};
 
std::tuple<float&, char&&, int> tpl(x, std::move(y), z);
const auto& [a, b, c] = tpl;
// using Tpl = const std::tuple<float&, char&&, int>;
// a names a structured binding that refers to x (initialized from get<0>(tpl))
// decltype(a) is std::tuple_element<0, Tpl>::type, i.e. float&
// b names a structured binding that refers to y (initialized from get<1>(tpl))
// decltype(b) is std::tuple_element<1, Tpl>::type, i.e. char&&
// c names a structured binding that refers to the third component of tpl, get<2>(tpl)
// decltype(c) is std::tuple_element<2, Tpl>::type, i.e. const int

[編集] ケース3: データメンバーへの束縛

Eのすべての非静的データメンバーは、Eの直接メンバーであるか、Eの同じ基底クラスのメンバーである必要があり、e.nameとして命名されたときに構造化束縛のコンテキストで整形式である必要があります。Eは匿名共用体メンバーを持つことはできません。Eの構造化束縛サイズは非静的データメンバーの数に等しくなります。

sb-identifier-list内の各構造化束縛は、宣言順序でeの次のメンバーを参照する左辺値の名前になります (ビットフィールドもサポートされます)。左辺値の型はe.mIの型であり、ここでmII番目のメンバーを指します。

I番目の構造化束縛の参照型は、参照型でない場合はe.mIの型であり、それ以外の場合はmIの宣言された型です。

#include <iostream>
 
struct S
{
    mutable int x1 : 2;
    volatile double y1;
};
 
S f() { return S{1, 2.3}; }
 
int main()
{
    const auto [x, y] = f(); // x is an int lvalue identifying the 2-bit bit-field
                             // y is a const volatile double lvalue
    std::cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // Error: y is const-qualified
    std::cout << x << ' ' << y << '\n';  // -2 2.3
}

[編集] 初期化順序

sb-identifier-list内のI番目の構造化束縛によって名前が付けられたオブジェクトまたは参照をvalIとします。

  • eの初期化は、いずれかのvalIの初期化よりも先行して順序付けられます
  • valIの初期化は、IJより小さい場合のいずれかのvalJの初期化よりも先行して順序付けられます。

[編集] 注釈

構造化束縛は制約できません。

template<class T>
concept C = true;
 
C auto [x, y] = std::pair{1, 2}; // error: constrained
(C++20以降)

メンバーgetの検索は、通常通りアクセス可能性を無視し、非型テンプレートパラメータの正確な型も無視します。たとえ整形式でなくても、privateなtemplate<char*> void get();メンバーが存在すると、メンバー解釈が使用されます。

[に先行する宣言部分は、導入された構造化束縛ではなく、隠された変数eに適用されます。

int a = 1, b = 2;
const auto& [x, y] = std::tie(a, b); // x and y are of type int&
auto [z, w] = std::tie(a, b);        // z and w are still of type int&
assert(&z == &a);                    // passes

std::tuple_size<E>valueという名前のメンバーを持つ完全型である場合、それがプログラムを整形式でないものにするとしても、タプルライクな解釈が常に使用されます。

struct A { int x; };
 
namespace std
{
    template<>
    struct tuple_size<::A> { void value(); };
}
 
auto [x] = A{}; // error; the "data member" interpretation is not considered.

ref-qualifierが存在し、expressionがprvalueの場合、一時オブジェクトへの参照束縛 (ライフタイム延長を含む) の通常の規則が適用されます。これらの場合、隠された変数eは、prvalue式から実体化された一時変数に束縛され、そのライフタイムを延長する参照です。通常通り、eが非const左辺値参照の場合、束縛は失敗します。

int a = 1;
 
const auto& [x] = std::make_tuple(a); // OK, not dangling
auto&       [y] = std::make_tuple(a); // error, cannot bind auto& to rvalue std::tuple
auto&&      [z] = std::make_tuple(a); // also OK

xが構造化束縛を表す場合のdecltype(x)は、その構造化束縛の参照型を名前付けます。タプルライクなケースでは、これはstd::tuple_elementによって返される型であり、このケースでは常に隠された参照が導入されるにもかかわらず、参照ではない場合があります。これは、非静的データメンバーがstd::tuple_elementによって返される型を持つ構造体に束縛する動作を効果的にエミュレートし、束縛自体の参照性は単なる実装の詳細となります。

std::tuple<int, int&> f();
 
auto [x, y] = f();       // decltype(x) is int
                         // decltype(y) is int&
 
const auto [z, w] = f(); // decltype(z) is const int
                         // decltype(w) is int&

構造化束縛はラムダ式によってキャプチャできません。

#include <cassert>
 
int main()
{
    struct S { int p{6}, q{7}; };
    const auto& [b, d] = S{};
    auto l = [b, d] { return b * d; }; // valid since C++20
    assert(l() == 42);
}
(C++20まで)


構造化束縛サイズは0であっても構いません。ただし、sb-identifier-listには、空の構造化束縛パックのみを導入できる識別子が厳密に1つだけ含まれている必要があります。

auto return_empty() -> std::tuple<>;
 
template <class>
void test_empty()
{
    auto [] = return_empty(); // error
    auto [...args] = return_empty(); // OK, args is an empty pack
    auto [one, ...rest] = return_empty(); // error, structured binding size is too small
}
(C++26以降)
機能テストマクロ 規格 機能
__cpp_structured_bindings 201606L (C++17) 構造化束縛
202403L (C++26) 属性付き構造化束縛
202406L (C++26) 条件としての構造化束縛宣言
202411L (C++26) 構造化束縛はパックを導入できます

[編集] キーワード

auto

[編集]

#include <iomanip>
#include <iostream>
#include <set>
#include <string>
 
int main()
{
    std::set<std::string> myset{"hello"};
 
    for (int i{2}; i; --i)
    {
        if (auto [iter, success] = myset.insert("Hello"); success) 
            std::cout << "Insert is successful. The value is "
                      << std::quoted(*iter) << ".\n";
        else
            std::cout << "The value " << std::quoted(*iter)
                      << " already exists in the set.\n";
    }
 
    struct BitFields
    {
        // C++20: default member initializer for bit-fields
        int b : 4 {1}, d : 4 {2}, p : 4 {3}, q : 4 {4};
    };
 
    {
        const auto [b, d, p, q] = BitFields{};
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
 
    {
        const auto [b, d, p, q] = []{ return BitFields{4, 3, 2, 1}; }();
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
 
    {
        BitFields s;
 
        auto& [b, d, p, q] = s;
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
 
        b = 4, d = 3, p = 2, q = 1;
        std::cout << s.b << ' ' << s.d << ' ' << s.p << ' ' << s.q << '\n';
    }
}

出力

Insert is successful. The value is "Hello".
The value "Hello" already exists in the set.
1 2 3 4
4 3 2 1
1 2 3 4
4 3 2 1

[編集] Defect reports (欠陥報告)

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 2285 C++17 expressionidentifier-listからの名前を参照する可能性があります 宣言は
この場合不適格となる
CWG 2312 C++17 ケース3でmutableの意味が失われた その意味は保持されています
CWG 2313 C++17 ケース2で、構造体束縛変数が再宣言される可能性があります 再宣言できません
CWG 2339 C++17 ケース2で、Iの定義が欠けていた 定義が追加されました
CWG 2341
(P1091R3)
C++17 構造化束縛はできませんでした
静的記憶域期間で宣言された
許可
CWG 2386 C++17 "タプルライクな"束縛プロトコルが使用された
std::tuple_size<E>が完全型である場合、常に
std::tuple_size<E>の場合にのみ使用されます
valueメンバーを持つ
CWG 2506 C++17 expressionがcv修飾された配列型の場合、
cv修飾がEに引き継がれた
そのcv修飾を破棄します
CWG 2635 C++20 構造化束縛は制約可能でした 禁止された
CWG 2867 C++17 初期化順序が不明確だった 明確化された
P0961R1 C++17 ケース2で、メンバーgetが使用された
あらゆる種類のgetが検索で見つかった場合
関数が見つかった場合のみ
非型パラメータを持つテンプレート
P0969R0 C++17 ケース3で、メンバーはpublicである必要があった アクセス可能であることのみが要求される
宣言のコンテキストで

[編集] 参照

  • C++23標準 (ISO/IEC 14882:2024)
  • 9.6 構造化束縛宣言 [dcl.struct.bind] (p: 228-229)
  • C++20 standard (ISO/IEC 14882:2020)
  • 9.6 構造化束縛宣言 [dcl.struct.bind] (p: 219-220)
  • C++17 standard (ISO/IEC 14882:2017)
  • 11.5 構造化束縛宣言 [dcl.struct.bind] (p: 219-220)

[編集] 関連項目

(C++11)
左辺値参照のtupleを生成するか、タプルを個別のオブジェクトにアンパックする
(関数テンプレート) [編集]
English 日本語 中文(简体) 中文(繁體)