9 Declarations [dcl.dcl]

9.6 Structured binding declarations [dcl.struct.bind]

A structured binding declaration introduces the identifiers , , of the sb-identifier-list as names.
An sb-identifier that contains an ellipsis introduces a structured binding pack ([temp.variadic]).
A structured binding is either an sb-identifier that does not contain an ellipsis or an element of a structured binding pack.
The optional attribute-specifier-seq of an sb-identifier appertains to the associated structured bindings.
Let cv denote the cv-qualifiers in the decl-specifier-seq and S consist of each decl-specifier of the decl-specifier-seq that is constexpr, constinit, or a storage-class-specifier.
A cv that includes volatile is deprecated; see [depr.volatile.type].
First, a variable with a unique name e is introduced.
If the assignment-expression in the initializer has array type cv1 A and no ref-qualifier is present, e is defined by
attribute-specifier-seq S cv A e ;
and each element is copy-initialized or direct-initialized from the corresponding element of the assignment-expression as specified by the form of the initializer.
Otherwise, e is defined as-if by where the declaration is never interpreted as a function declaration and the parts of the declaration other than the declarator-id are taken from the corresponding structured binding declaration.
The type of the id-expression e is called E.
[Note 1: 
E is never a reference type ([expr.prop]).
— end note]
The structured binding size of E, as defined below, is the number of structured bindings that need to be introduced by the structured binding declaration.
If there is no structured binding pack, then the number of elements in the sb-identifier-list shall be equal to the structured binding size of E.
Otherwise, the number of non-pack elements shall be no more than the structured binding size of E; the number of elements of the structured binding pack is the structured binding size of E less the number of non-pack elements in thesb-identifier-list.
Let denote the structured binding in the structured binding declaration after expanding the structured binding pack, if any.
[Note 2: 
If there is no structured binding pack, then denotes .
— end note]
[Example 1: struct C { int x, y, z; }; template<class T> void now_i_know_my() { auto [a, b, c] = C(); // OK, is a, is b, and is c auto [d, ...e] = C(); // OK, is d, the pack e contains two structured bindings: and auto [...f, g] = C(); // OK, the pack f contains two structured bindings: and , and is g 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 } — end example]
If a structured binding declaration appears as a condition, the decision variable ([stmt.pre]) of the condition is e.
If the initializer refers to one of the names introduced by the structured binding declaration, the program is ill-formed.
If E is an array type with element type T, the structured binding size of E is equal to the number of elements of E.
Each is the name of an lvalue that refers to the element i of the array and whose type is T; the referenced type is T.
[Note 3: 
The top-level cv-qualifiers of T are cv.
— end note]
[Example 2: auto f() -> int(&)[2]; auto [ x, y ] = f(); // x and y refer to elements in a copy of the array return value auto& [ xr, yr ] = f(); // xr and yr refer to elements in the array referred to by f's return value auto g() -> int(&)[4]; template<size_t N> void h(int (&arr)[N]) { auto [a, ...b, c] = arr; // a names the first element of the array, b is a pack referring to the second and // third elements, and c names the fourth element auto& [...e] = arr; // e is a pack referring to the four elements of the array } void call_h() { h(g()); } — end example]
Otherwise, if the qualified-id std​::​tuple_size<E> names a complete class type with a member named value, the expression std​::​tuple_size<E>​::​value shall be a well-formed integral constant expression and the structured binding size of E is equal to the value of that expression.
Let i be an index prvalue of type std​::​size_t corresponding to .
If a search for the name get in the scope of E ([class.member.lookup]) finds at least one declaration that is a function template whose first template parameter is a non-type parameter, the initializer is e.get<i>().
Otherwise, the initializer is get<i>(e), where get undergoes argument-dependent lookup ([basic.lookup.argdep]).
In either case, get<i> is interpreted as a template-id.
[Note 4: 
Ordinary unqualified lookup is not performed.
— end note]
In either case, e is an lvalue if the type of the entity e is an lvalue reference and an xvalue otherwise.
Given the type designated by std​::​tuple_element<i, E>​::​type and the type designated by either & or &&, where is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise, variables are introduced with unique names as follows:
S U r = initializer ;
Each is the name of an lvalue of type that refers to the object bound to ; the referenced type is .
The initialization of e and any conversion of e considered as a decision variable ([stmt.pre]) is sequenced before the initialization of any .
The initialization of each is sequenced before the initialization of any where .
Otherwise, all of E's non-static data members shall be direct members of E or of the same base class of E, well-formed when named as e.name in the context of the structured binding, E shall not have an anonymous union member, and the structured binding size of E is equal to the number of non-static data members of E.
Designating the non-static data members of E as , , (in declaration order), each is the name of an lvalue that refers to the member m of e and whose type is that of e. ([expr.ref]); the referenced type is the declared type of if that type is a reference type, or the type of e. otherwise.
The lvalue is a bit-field if that member is a bit-field.
[Example 3: struct S { mutable int x1 : 2; volatile double y1; }; S f(); const auto [ x, y ] = f();
The type of the id-expression x is “int”, the type of the id-expression y is “const volatile double.
— end example]