A structured binding declaration introduces the identifiers v0, v1, v2, ... of the identifier-list as names ([basic.scope.declarative]), called structured bindings. Let cv denote the cv-qualifiers in the decl-specifier-seq. 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 has type cv A 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
attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt e initializer ;
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: E is never a reference type (Clause [expr]). βββend noteβ]
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 vi 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: The top-level cv-qualifiers of T are cv. βββend noteβ] [βExample:
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 type, 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. The unqualified-id get is looked up in the scope of E by class member access lookup, and if that finds at least one declaration, the initializer is e.get<i>(). Otherwise, the initializer is get<i>(e), where get is looked up in the associated namespaces. In either case, get<i> is interpreted as a template-id. [βNote: 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 Ti designated by stdβ::βtuple_Βelement<i, E>β::βtype, each vi is a variable of type βreference to Tiβ initialized with the initializer, where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise; the referenced type is Ti.
Otherwise, all of E's non-static data members shall be public direct members of E or of the same unambiguous public base class of E, 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 m0, m1, m2, ... (in declaration order), each vi is the name of an lvalue that refers to the member mi of e and whose type is cv Ti, where Ti is the declared type of that member; the referenced type is cv Ti. The lvalue is a bit-field if that member is a bit-field. [βExample:
struct S { int x1 : 2; volatile double y1; }; S f(); const auto [ x, y ] = f();
The type of the id-expression x is βconst intβ, the type of the id-expression y is βconst volatile doubleβ. βββend exampleβ]