Espacios de nombres
Variantes
Acciones

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

De cppreference.com
< cpp‎ | language
 
 
 
 

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-autooperador-ref(opcional)[lista-de-identificadores]=expresión; (1)
atrib(opcional)cv-autooperador-ref(opcional)[lista-de-identificadores]{expresión}; (2)
atrib(opcional)cv-autooperador-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-almacenamientostatic 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 arrayA y no está presente el operador-ref, entonces e tiene tipo cvA, 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.

Contenido

[editar]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 arrayE 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]

[editar]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,std::move(y),z);constauto&[a,b,c]= tpl;// a denomina vínculo estructurado que se refiere a x; decltype(a) es float&// b denomina vínculo estructurado que se refiere a y; decltype(b) es char&&// c denomina vínculo estructurado que se refiere al 3er elemento de tpl; decltype(c) es const int

[editar]Caso 3: Vincular datos miembro

Cada dato miembro no estático de E debe ser un miembro directo de E o de la misma clase base de E, y debe estar bien formado en el contexto del vínculo estructurado al ser denominado e.nombre. E no puede tener un miembro que es una unión anónima. El número de identificadores debe ser igual al número de datos miembro no estáticos.

Cada identificador en la lista-de-identificadores se vuelve el nombre de un lvalue que se refiere al próximo miembro de e en el orden de declaración (se soportan los campos de bits); el tipo del lvalue es cv T_i, donde cv son los calificadores de E y T_i es el tipo declarado del enésimo miembro.

El tipo referenciado del enésimo identificador es cv T_i.

struct S {int x1 :2;volatiledouble y1;}; S f();   constauto[x, y]= f();// x es un lvalue const int que identifica el campo de bits de dos bits// y es un lvalue const volatile double

[editar]Notas

La búsqueda del miembro get ignora la accesibilidad como de costumbre y también ignora el tipo exacto del parámetro de plantilla sin tipo. Un miembro template<char*>void get(); privado hará que se utilice la interpretación del miembro, aunque esté mal formada.

La parte de la declaración que precede a [ se aplica a la variable oculta e, no a los identificadores introducidos.

int a =1, b =2;constauto&[x, y]=std::tie(a, b);// x e y son de tipo int&auto[z, w]=std::tie(a, b);// z y w todavía son de tipo int&assert(&z ==&a);// pasa

Siempre se usa la interpretación similar a una tupla si std::tuple_size<E> es un tipo completo, incluso si eso ocasionaría que el programa esté mal formado:

struct A {int x;};namespace std {template<>struct tuple_size<::A>{};}   auto[x]= A{};// ERROR; no se considera la interpretación del "dato miembro".

Las reglas habituales para la vinculación de referencias a temporales (incluida la extensión de la vida útil) se aplican si un operador-ref está presente y la expresión es un prvalue. En esos casos, la variable oculta e es una referencia que se vincula a la variable temporal materializada de la expresión prvalue, extendiendo su vida. Como de costumbre, el vínculo fallará si e es una referencia lvalue no constante:

int a =1;constauto&[x]=std::make_tuple(a);// de acuerdo, no está pendienteauto&[y]=std::make_tuple(a);// ERROR, no se puede vincular auto& a rvalue std::tupleauto&&[z]=std::make_tuple(a);// también de acuerdo

decltype(x), donde x denota un vínculo estructurado, denomina el tipo referenciado de ese vínculo estructurado. En el caso similar a una tupla, este es el tipo devuelto por std::tuple_element, que puede no ser una referencia aun cuando en este caso siempre se introduce una referencia oculta. Esto efectivamente emula el comportamiento de vincular una estructura cuyos datos miembro no estáticos tienen los tipos devueltos por tuple_element, siendo la referencialidad del enlace en sí un mero detalle de implementación.

std::tuple<int, int&> f();   auto[x, y]= f();// decltype(x) es int// decltype(y) es int&   constauto[z, w]= f();// decltype(z) es const int// decltype(w) es int&

[editar]Ejemplo

#include <set>#include <string>#include <iomanip>#include <iostream>   int main(){std::set<std::string> myset; si (auto[iter, success]= myset.insert("Hola"); success)std::cout<<"La insercion tuvo exito. El valor es "<<std::quoted(*iter)<<'\n';elsestd::cout<<"El valor "<<std::quoted(*iter)<<" ya existe en el conjunto\n";}

Salida:

La insercion tuvo exito. El valor es "Hola"

[editar]Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo publicado Comportamiento correcto
P0961R1 C++17 En el caso similar a una tupla, el miembro get se usa si l
búsqueda encuentra un get de cualquier tipo.
Solo si la búsqueda encuentra una plantilla de
función con un parámetro sin tipo.
P0969R0 C++17 En el caso de vinculación a miembros, se requiere que
los miembros sean públicos.
Solo se requiere que sean accesibles en el
contexto de la declaración.
CWG 2386 C++17 El protocolo de vinculación para "similar a una tupla" se usa
cuando tuple_size<E> es un tipo completo.
Se usa solo cuando tuple_size<E> tiene un
miembrovalue.

[editar]Véase también

Crea una tupla de referencias lvalue o desempaca una tupla en objetos individuales.
(plantilla de función)[editar]
close