9 Declarations [dcl.dcl]

9.5 Structured binding declarations [dcl.struct.bind]

A structured binding declaration introduces the identifiers , , of the identifier-list as names ([basic.scope.declarative]) of 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-seq decl-specifier-seq ref-qualifier 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 ([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
:
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.
Let i be an index prvalue of type std::size_­t corresponding to .
The unqualified-id get is looked up in the scope of E by class member access lookup ([basic.lookup.classref]), and if that 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 is looked up in the associated namespaces ([basic.lookup.argdep]).
In either case, get<i> is interpreted as a template-id.
[Note
:
Ordinary unqualified lookup ([basic.lookup.unqual]) 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, variables are introduced with unique names of type “reference to ” initialized with the initializer ([dcl.init.ref]), where the reference is an lvalue reference if the initializer is an lvalue and an rvalue reference otherwise.
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 cv , where is the declared type of that member; the referenced type is cv .
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
]