9 Declarations [dcl.dcl]

9.6 Structured binding declarations [dcl.struct.bind]

A structured binding declaration introduces the identifiers , , of the identifier-list as names of structured bindings.
Let cv denote the cv-qualifiers in the decl-specifier-seq and S consist of the storage-class-specifiers of the decl-specifier-seq (if any).
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 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]
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 number of elements in the identifier-list shall be equal to the number of elements of E.
Each v 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 2:
The top-level cv-qualifiers of T are cv.
— end note]
[Example 1: 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 — 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 number of elements in the identifier-list shall be 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 3:
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 .
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 number of elements in the identifier-list shall be equal to the number of non-static data members of E.
Designating the non-static data members of E as , , (in declaration order), each v 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 2: 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]