 Structured binding declaration (since C++17)
 Binds the specified names to subobjects or elements of the initializer.
Like a reference, a structured binding is an alias to an existing object. Unlike a reference, a structured binding does not have to be of a reference type.
   attr(optional) cv-auto ref-qualifier(optional) [ identifier-list ] = expression ;   (1)   
   attr(optional) cv-auto ref-qualifier(optional) [ identifier-list ]{ expression };   (2)   
   attr(optional) cv-auto ref-qualifier(optional) [ identifier-list ]( expression );   (3)   

    attr   -   sequence of any number of attributes 
   cv-auto   -   possibly cv-qualified type specifier auto, may also include storage-class-specifier static or thread_local; including volatile in cv-qualifiers is deprecated (since C++20) 
   ref-qualifier   -   either & or && 
   identifier-list   -   list of comma-separated identifiers introduced by this declaration 
   expression   -   an expression that does not have the comma operator at the top level (grammatically, an assignment-expression), and has either array or non-union class type. If expression refers to any of the names from identifier-list, the declaration is ill-formed. 

 A structured binding declaration introduces all identifiers in the identifier-list as names in the surrounding scope and binds them to subobjects or elements of the object denoted by expression. The bindings so introduced are called structured bindings.
A structured binding declaration first introduces a uniquely-named variable (here denoted by e) to hold the value of the initializer, as follows:

 If expression has array type A and no ref-qualifier is present, then e has type cv A, where cv is the cv-qualifiers in the cv-auto sequence, and each element of e is copy- (for (1)) or direct- (for (2,3)) initialized from the corresponding element of expression. 
 Otherwise e is defined as if by using its name instead of [ identifier-list ] in the declaration. 
 We use E to denote the type of the expression e. (In other words, E is the equivalent of st8b7c:f320:99b9:690f:4595:cd17:293a:c069remove_reference_t<decltype((e))>.).
A structured binding declaration then performs the binding in one of three possible ways, depending on E:

 Case 1: if E is an array type, then the names are bound to the array elements. 
 Case 2: if E is a non-union class type and st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_size<E> is a complete type with a member named value (regardless of the type or accessibility of such member), then the "tuple-like" binding protocol is used. 
 Case 3: if E is a non-union class type but st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_size<E> is not a complete type, then the names are bound to the accessible data members of E. 
 Each of the three cases is described in more detail below.
Each structured binding has a referenced type, defined in the description below. This type is the type returned by decltype when applied to an unparenthesized structured binding.
 Case 1: binding an array Each identifier in the identifier-list becomes the name of an lvalue that refers to the corresponding element of the array. The number of identifiers must equal the number of array elements.
The referenced type for each identifier is the array element type. Note that if the array type E is cv-qualified, so is its element type.
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]  Case 2: binding a tuple-like type The expression st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_size<E>8b7c:f320:99b9:690f:4595:cd17:293a:c069value must be a well-formed integer constant expression, and the number of identifiers must equal st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_size<E>8b7c:f320:99b9:690f:4595:cd17:293a:c069value.
For each identifier, a variable whose type is "reference to st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element<i, E>8b7c:f320:99b9:690f:4595:cd17:293a:c069type" is introduced: lvalue reference if its corresponding initializer is an lvalue, rvalue reference otherwise. The initializer for the i-th variable is.

 e.get<i>(), if lookup for the identifier get in the scope of E by class member access lookup finds at least one declaration that is a function template whose first template parameter is a non-type parameter 
 Otherwise, get<i>(e), where get is looked up by argument-dependent lookup only, ignoring non-ADL lookup. 
 In these initializer expressions, e is an lvalue if the type of the entity e is an lvalue reference (this only happens if the ref-qualifier is & or if it is && and the initializer expression is an lvalue) and an xvalue otherwise (this effectively performs a kind of perfect forwarding), i is a st8b7c:f320:99b9:690f:4595:cd17:293a:c069size_t prvalue, and <i> is always interpreted as a template parameter list.
The variable has the same storage duration as e.
The identifier then becomes the name of an lvalue that refers to the object bound to said variable.
The referenced type for the i-th identifier is st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element<i, E>8b7c:f320:99b9:690f:4595:cd17:293a:c069type.
float x{};
char  y{};
int   z{};
 
st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple<float&, char&&, int> tpl(x, st8b7c:f320:99b9:690f:4595:cd17:293a:c069move(y), z);
const auto& [a, b, c] = tpl;
// using Tpl = const st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple<float&, char&&, int>;
// a names a structured binding that refers to x (initialized from get<0>(tpl))
// decltype(a) is st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element<0, Tpl>8b7c:f320:99b9:690f:4595:cd17:293a:c069type, i.e. float&
// b names a structured binding that refers to y (initialized from get<1>(tpl))
// decltype(b) is st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element<1, Tpl>8b7c:f320:99b9:690f:4595:cd17:293a:c069type, i.e. char&&
// c names a structured binding that refers to the third component of tpl, get<2>(tpl)
// decltype(c) is st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element<2, Tpl>8b7c:f320:99b9:690f:4595:cd17:293a:c069type, i.e. const int  Case 3: binding to data members Every non-static data member of E must be a direct member of E or the same base class of E, and must be well-formed in the context of the structured binding when named as e.name. E may not have an anonymous union member. The number of identifiers must equal the number of non-static data members.
Each identifier in identifier-list becomes the name of an lvalue that refers to the next member of e in declaration order (bit fields are supported); the type of the lvalue is that of e.m_i, where m_i refers to the i-th member.
The referenced type of the i-th identifier is the type of e.m_i if it is not a reference type, or the declared type of m_i otherwise.
 #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
    st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // Error: y is const-qualified
    st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << x << ' ' << y << '\n';  // -2 2.3
} Output:
1 2.3
-2 2.3   Notes The lookup for member get ignores accessibility as usual and also ignores the exact type of the non-type template parameter. A private template<char*> void get(); member will cause the member interpretation to be used, even though it is ill-formed.
The portion of the declaration preceding [ applies to the hidden variable e, not to the introduced identifiers.
int a = 1, b = 2;
const auto& [x, y] = st8b7c:f320:99b9:690f:4595:cd17:293a:c069tie(a, b); // x and y are of type int&
auto [z, w] = st8b7c:f320:99b9:690f:4595:cd17:293a:c069tie(a, b);        // z and w are still of type int&
assert(&z == &a);                    // passes The tuple-like interpretation is always used if st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_size<E> is a complete type, even if that would cause the program to be ill-formed:
struct A { int x; };
 
namespace std
{
    template<>
    struct tuple_size<8b7c:f320:99b9:690f:4595:cd17:293a:c069> {};
}
 
auto [x] = A{}; // error; the "data member" interpretation is not considered. The usual rules for reference-binding to temporaries (including lifetime-extension) apply if a ref-qualifier is present and the expression is a prvalue. In those cases the hidden variable e is a reference that binds to the temporary variable materialized from the prvalue expression, extending its lifetime. As usual, the binding will fail if e is a non-const lvalue reference:
int a = 1;
 
const auto& [x] = st8b7c:f320:99b9:690f:4595:cd17:293a:c069make_tuple(a); // OK, not dangling
auto&       [y] = st8b7c:f320:99b9:690f:4595:cd17:293a:c069make_tuple(a); // error, cannot bind auto& to rvalue st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple
auto&&      [z] = st8b7c:f320:99b9:690f:4595:cd17:293a:c069make_tuple(a); // also OK decltype(x), where x denotes a structured binding, names the referenced type of that structured binding. In the tuple-like case, this is the type returned by st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple_element, which may not be a reference even though a hidden reference is always introduced in this case. This effectively emulates the behavior of binding to a struct whose non-static data members have the types returned by tuple_element, with the referenceness of the binding itself being a mere implementation detail.
st8b7c:f320:99b9:690f:4595:cd17:293a:c069tuple<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&  
 Structured bindings cannot be captured by lambda expressions.
 (until C++20)
   #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);
}   Example  #include <set>
#include <string>
#include <iomanip>
#include <iostream>
 
int main()
{
    st8b7c:f320:99b9:690f:4595:cd17:293a:c069set<st8b7c:f320:99b9:690f:4595:cd17:293a:c069string> myset{"hello"};
 
    for (int i{2}; i; --i)
    {
        if (auto [iter, success] = myset.insert("Hello"); success) 
            st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << "Insert is successful. The value is "
                      << st8b7c:f320:99b9:690f:4595:cd17:293a:c069quoted(*iter) << ".\n";
        else
            st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << "The value " << st8b7c:f320:99b9:690f:4595:cd17:293a:c069quoted(*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{};
        st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
 
    {
        const auto [b, d, p, q] = []{ return BitFields{4, 3, 2, 1}; }();
        st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
 
    {
        BitFields s;
 
        auto& [b, d, p, q] = s;
        st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
 
        b = 4, d = 3, p = 2, q = 1;
        st8b7c:f320:99b9:690f:4595:cd17:293a:c069cout << s.b << ' ' << s.d << ' ' << s.p << ' ' << s.q << '\n';
    }
} Output:
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 The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
   DR   Applied to   Behavior as published   Correct behavior 
   CWG 2285   C++17   expression could refer to the names from identifier-list   the declaration is ill-formed in this case 
   P0961R1   C++17   in the tuple-like case, member get is usedif lookup finds a get of whatever kind   only if lookup finds a function templatewith a non-type parameter 
   P0969R0   C++17   in the binding-to-members case,the members are required to be public   only required to be accessiblein the context of the declaration 
   CWG 2312   C++17   the meaning of mutable was lostin the binding-to-members case   its meaning is still kept 
   CWG 2386   C++17   the "tuple-like" binding protocol is usedwhenever tuple_size<E> is a complete type   used only when tuple_size<E>has a member value 

  References 
 C++20 standard (ISO/IEC 14882:2020): 
 9.6 Structured binding declarations [dcl.struct.bind] (p: 219-220) 
 C++17 standard (ISO/IEC 14882:2017): 
 11.5 Structured binding declarations [dcl.struct.bind] (p: 219-220) 
  See also     tie
(C++11)   creates a tuple of lvalue references or unpacks a tuple into individual objects  (function template) 
  

    
{'path': 'public/docs/cpp/language/structured_binding.html', 'source': 'DevDocs.IO'}