Espacios de nombres
Variantes

Declaración de vínculo estructurado (desde C++17)

De cppreference.com
 
 
 
 

Vincula los nombres especificados a subobjetos o elementos del inicializador.

Como una referencia, un vínculo estructurado es un alias a un objeto existente. A diferencia de una referencia, el tipo de enlace vinculado no tiene que ser un tipo referencia.

atrib(opcional) cv-auto operador-ref(opcional) [ lista-de-identificadores ] = expresión ; (1)
atrib(opcional) cv-auto operador-ref(opcional) [ lista-de-identificadores ] { expresión } ; (2)
atrib(opcional) cv-auto operador-ref(opcional) [ lista-de-identificadores ] ( expresión ) ; (3)
atrib - Secuencia de cualquier número de atributos.
cv-auto - Especificador de tipo posiblemente calificado-cv auto, también puede incluir especificador-de-clase-de-almacenamiento static o thread_local; incluir volatile en calificadores-cv está en desuso. (desde C++20)
operador-ref - Ya sea & o &&.
lista-de-identificadores - Lista de identificadores separados por comas introducidos por esta declaración.
expresión - Una expresión que no tiene el operador coma en el nivel más alto (gramaticalmente, una expresión-de-asignación), y tiene ya sea un tipo array o un tipo clase que no es una unión. Si expresión se refiere a cualquiera de los nombres de la lista-de-identificadores, la declaración está mal formada.

Una declaración de vínculo estructurado introduce todos los identificadores en la lista-de-identificadores como nombres en el ámbito circundante y los vincula a subobjetos o elementos del objeto denotado por expresión. Los vínculos así introducidos se denominan "vínculos estructurados".

Una declaración de vínculo estructurado primero introduce una variable con un nombre único (denotada aquí por e) para albergar el valor del inicializador, de la manera siguiente:

  • Si expresión tiene tipo array A y no está presente el operador-ref, entonces e tiene tipo cv A, donde cv son los calificadores-cv en la secuencia cv-auto, y cada elemento de e es inicializado mediante la inicialización por copia (para la forma (1)) o directa (para las formas (2,3)) a partir del elemento correspondiente de la expresión.
  • De lo contrario e se define como si se usara su nombre en lugar de la [ lista-de-identificadores ] en la declaración.

Usamos E para denotar el tipo de la expresión e (en otras palabras, E es el equivalente de std::remove_reference_t<decltype((e))>).

Una declaración de vínculo estructurado entonces lleva a cabo la vinculación en una de tres maneras posibles, dependiendo de E:

  • Caso 1: Si E es un tipo array, entonces los nombres se vinculan a los elementos del array.
  • Caso 2: Si E es un tipo clase que no es una unión y std::tuple_size<E> es un tipo completo con un miembro denominado value (independientemente del tipo o accesibilidad de tal miembro), entonces se usa el protocolo de vinculación "similar a una tupla".
  • Caso 3: Si E es un tipo clase que no es una unión pero std::tuple_size<E> no es un tipo completo, entonces los nombres se vinculan a los datos miembro accesibles de E.

Cada uno de los tres casos se describe con más detalle más abajo.

Cada vínculo estructurado tiene un tipo referenciado, definido en la descripción más abajo. Este tipo es el tipo devuelto por decltype cuando se aplica a un vínculo estructurado sin paréntesis.

Caso 1: Vincular un array

Cada identificador en la lista-de-identificadores se vuelve el nombre de un lvalue que se refiere al elemento correspondiente del array. El número de identificadores debe ser igual al número de elementos del array.

El tipo referenciado para cada identificador es el tipo del elemento del array. Observa que si el tipo array E está calificado-cv, también lo está su tipo de elemento.

int a[2] = {1,2};

auto [x,y] = a; // crea e[2], copia a en e, entonces x se refiere a e[0], e y se refiere a e[1]
auto& [xr, yr] = a; // xr se refiere a a[0], yr se refiere a a[1]

Caso 2: Vincular un tipo similar a una tupla

La expresión std::tuple_size<E>::value debe ser una expresión constante entera bien formada, y el número de identificadores debe ser igual a std::tuple_size<E>::value.

Para cada identificador, se introduce una variable cuyo tipo es una "referencia a std::tuple_element<i, E>::type": una referencia lvalue si su inicializador correspondiente es un lvalue, de lo contrario, una referencia rvalue. El inicializador para la enésima variable es

  • e.get<i>(), si la búsqueda del identificador get en el ámbito de E mediante la búsqueda de acceso a miembro de clase encuentra al menos una declaración que es una plantilla de función cuyo primer parámetro de plantilla es un parámetro sin tipo;
  • de lo contrario, get<i>(e), donde get se busca solo mediante la búsqueda dependiente de argumento ignorando la búsqueda que no es dependiente de argumento.

En estas expresiones inicializadoras, e es un lvalue si el tipo de la entidad e es una referencia lvalue (esto solamence sucede si el operador-ref es & o si es && y la expresión inicializadora es un lvalue) y un xvalue de lo contrario (esto efectivamente realiza una especie de reenvío perfecto), i es un std::size_t prvalue, y <i> siempre se interpreta como una lista de parámetros de plantilla.

La variable tiene la misma duración de almacenamiento que e.

Entonces el identificador se vuelve el nombre de un lvalue que se refiere al objeto vinculado a dicha variable.

El tipo referenciado para el enésimo identificador es std::tuple_element<i, E>::type.

float x{};
char  y{};
int   z{};

std::tuple<float&,char&&,int> tpl(x