Namespaces
Variants

Structured binding declaration (since C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Vincula los nombres especificados a subobjetos o elementos del inicializador.

Al igual que una referencia, un enlace estructurado es un alias para un objeto existente. A diferencia de una referencia, un enlace estructurado no tiene que ser de tipo referencia.

attr  (opcional) decl-specifier-seq ref-qualifier  (opcional) [ sb-identifier-list ] initializer  ;
attr - secuencia de cualquier número de atributos
decl-specifier-seq - secuencia de los siguientes especificadores (siguiendo las reglas de declaración simple ):
(desde C++26)
ref-qualifier - ya sea & o &&
sb-identifier-list - lista de identificadores separados por comas introducidos por esta declaración , cada identificador puede ir seguido de una secuencia de especificadores de atributos (desde C++26)
initializer - un inicializador (ver abajo)


initializer puede ser uno de los siguientes:

= expresión (1)
{ expresión } (2)
( expresión ) (3)
expression - cualquier expresión (excepto expresiones de coma sin paréntesis)


Una declaración de enlace estructurado introduce todos los identificadores en la sb-identifier-list como nombres en el ámbito circundante y los vincula a subobjetos o elementos del objeto denotado por expression . Los enlaces así introducidos se denominan enlaces estructurados .

Uno de los identificadores en la lista-de-identificadores-sb puede ir precedido por una elipsis. Dicho identificador introduce un paquete de enlace estructurado .

El identificador debe declarar una entidad con plantilla .

(since C++26)

Un enlace estructurado es un identificador en la lista-de-identificadores-sb  que no está precedido por una elipsis, o un elemento de un paquete de enlace estructurado introducido en la misma lista de identificadores (desde C++26) .

Contenidos

Proceso de vinculación

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

  • Si expression tiene tipo array cv1 A y no está presente ningún ref-qualifier , defina e como attr  (opcional) specifiers A e; , donde specifiers es una secuencia de los especificadores en decl-specifier-seq excluyendo auto .
Entonces cada elemento de e se inicializa desde el elemento correspondiente de expression según lo especificado por la forma del initializer :
  • De lo contrario, defina e como attr  (opcional) decl-specifier-seq ref-qualifier  (opcional) e initializer  ; .

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

Un tamaño de enlace estructurado de E es el número de enlaces estructurados que deben introducirse mediante la declaración de enlace estructurado.

El número de identificadores en sb-identifier-list debe ser igual al tamaño de enlace estructurado de E .

(until C++26)

Dado el número de identificadores en sb-identifier-list como N y el tamaño de enlace estructurado de E como S :

  • Si no hay un pack de enlace estructurado, N debe ser igual a S .
  • De lo contrario, el número de elementos no-pack (es decir, N - 1 ) debe ser menor o igual que S , y el número de elementos del pack de enlace estructurado es S - N + 1 (que puede ser cero).
(since C++26)
struct C { int x, y, z; };
template<class T>
void now_i_know_my() 
{
    auto [a, b, c] = C(); // CORRECTO: a, b, c se refieren a x, y, z, respectivamente
    auto [d, ...e] = C(); // CORRECTO: d se refiere a x; ...e se refiere a y y z
    auto [...f, g] = C(); // CORRECTO: ...f se refiere a x e y; g se refiere a z
    auto [h, i, j, ...k] = C();    // CORRECTO: el paquete k está vacío
    auto [l, m, n, o, ...p] = C(); // error: el tamaño del enlace estructurado es demasiado pequeño
}

Una declaración de enlace estructurado realiza el enlace de 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 no-union y std:: tuple_size < E > es un tipo completo con un miembro llamado value (independientemente del tipo o accesibilidad de dicho miembro), entonces se utiliza el protocolo de vinculación "tipo-tupla".
  • Caso 3: Si E es un tipo clase no-union pero std:: tuple_size < E > no es un tipo completo, entonces los nombres se vinculan a los miembros de datos accesibles de E .

Cada uno de los tres casos se describe con más detalle a continuación.

Cada enlace estructurado tiene un tipo referenciado , definido en la descripción a continuación. Este tipo es el tipo devuelto por decltype cuando se aplica a un enlace estructurado sin paréntesis.

Caso 1: vinculando un array

Cada enlace estructurado en la lista-de-identificadores-sb se convierte en el nombre de un lvalue que hace referencia al elemento correspondiente del array. El tamaño del enlace estructurado de E es igual al número de elementos del array.

El tipo referenciado para cada enlace estructurado es el tipo de elemento del array. Nótese que si el tipo de 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,
                    // luego x se refiere a e[0], y se refiere a e[1]
auto& [xr, yr] = a; // xr se refiere a a[0], yr se refiere a a[1]

Caso 2: vinculación de un tipo que implementa las operaciones de tupla

La expresión std:: tuple_size < E > :: value debe ser una expresión constante integral bien formada, y el tamaño del enlace estructurado de E es igual a std:: tuple_size < E > :: value .

Para cada enlace estructurado, se introduce una variable cuyo tipo es "referencia a std:: tuple_element < I, E > :: type ": referencia lvalue si su inicializador correspondiente es un lvalue, referencia rvalue en caso contrario. El inicializador para la I -ésima variable es

  • e. get < I > ( ) , si la búsqueda del identificador get en el ámbito de E mediante 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 constante
  • De lo contrario, get < I > ( e ) , donde get se busca únicamente mediante búsqueda dependiente de argumento , ignorando la búsqueda no-ADL.

En estas expresiones de inicialización, e es un lvalue si el tipo de la entidad e es una referencia lvalue (esto solo ocurre si el ref-qualifier es & o si es && y la expresión de inicialización es un lvalue) y un xvalue en caso contrario (esto efectivamente realiza una especie de perfect forwarding), I es un prvalue de tipo std::size_t , y < I > siempre se interpreta como una lista de parámetros de plantilla.

La variable tiene la misma storage duration que e .

La vinculación estructurada se convierte entonces en el nombre de un lvalue que se refiere al objeto vinculado a dicha variable.

El tipo referenciado para el I -ésimo structured binding es std:: tuple_element < I, E > :: type .

float x{};
char  y{};
int   z{};
std::tuple<float&, char&&, int> tpl(x, std::move(y), z);
const auto& [a, b, c] = tpl;
// usando Tpl = const std::tuple<float&, char&&, int>;
// a nombra un enlace estructurado que se refiere a x (inicializado desde get<0>(tpl))
// decltype(a) es std::tuple_element<0, Tpl>::type, es decir float&
// b nombra un enlace estructurado que se refiere a y (inicializado desde get<1>(tpl))
// decltype(b) es std::tuple_element<1, Tpl>::type, es decir char&&
// c nombra un enlace estructurado que se refiere al tercer componente de tpl, get<2>(tpl)
// decltype(c) es std::tuple_element<2, Tpl>::type, es decir const int

Caso 3: vinculación a miembros de datos

Cada miembro de datos 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 enlace estructurado cuando se nombra como e. name . E no puede tener un miembro de unión anónima. El tamaño del enlace estructurado de E es igual al número de miembros de datos no estáticos.

Cada enlace estructurado en sb-identifier-list se convierte en el nombre de un lvalue que hace referencia al siguiente miembro de e en orden de declaración (los campos de bits están soportados); el tipo del lvalue es el de e. mI , donde mI hace referencia al I ésimo miembro.

El tipo referenciado del I -ésimo enlace estructurado es el tipo de e. mI si no es un tipo referencia, o el tipo declarado de mI en caso contrario.

#include <iostream>
struct S
{
    mutable int x1 : 2;
    volatile double y1;
};
S f() { return S{1, 2.3}; }
int main()
{
    const auto [x, y] = f(); // x es un lvalue int que identifica el campo de bits de 2 bits
                             // y es un lvalue double const volatile
    std::cout << x << ' ' << y << '\n';  // 1 2.3
    x = -2;   // OK
//  y = -2.;  // Error: y está calificado como const
    std::cout << x << ' ' << y << '\n';  // -2 2.3
}

Orden de inicialización

Sea valI el objeto o referencia denominado por el I ésimo enlace estructurado en sb-identifier-list :

  • La inicialización de e está secuenciada antes que la inicialización de cualquier valI .
  • La inicialización de cada valI está secuenciada antes que la inicialización de cualquier valJ donde I es menor que J .

Notas

Los enlaces estructurados no pueden ser restringidos :

template<class T>
concept C = true;
C auto [x, y] = std::pair{1, 2}; // error: constrained
(desde C++20)

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

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

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

La interpretación similar a una tupla siempre se utiliza si std:: tuple_size < E > es un tipo completo con un miembro llamado value , incluso si eso causaría que el programa esté mal formado:

struct A { int x; };
namespace std
{
    template<>
    struct tuple_size<::A> { void value(); };
}
auto [x] = A{}; // error; la interpretación de "miembro de datos" no se considera.

Las reglas habituales para el enlace de referencias a temporales (incluyendo la extensión de vida) se aplican si un ref-qualifier está presente y la expresión es un prvalue. En esos casos la variable oculta e es una referencia que se enlaza a la variable temporal materializada a partir de la expresión prvalue, extendiendo su tiempo de vida. Como es habitual, el enlace fallará si e es una referencia lvalue no constante:

int a = 1;
const auto& [x] = std::make_tuple(a); // CORRECTO, no es un puntero colgante
auto&       [y] = std::make_tuple(a); // error, no se puede vincular auto& a rvalue std::tuple
auto&&      [z] = std::make_tuple(a); // también CORRECTO

decltype ( x ) , donde x denota un enlace estructurado, nombra el tipo referenciado de ese enlace estructurado. En el caso tipo-tupla, este es el tipo devuelto por std::tuple_element , que puede no ser una referencia aunque en este caso siempre se introduce una referencia oculta. Esto emula efectivamente el comportamiento de enlazar a una estructura cuyos miembros de datos no estáticos tienen los tipos devueltos por std::tuple_element , siendo la referencia 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&
const auto [z, w] = f(); // decltype(z) es const int
                         // decltype(w) es int&

Los enlaces estructurados no pueden ser capturados por expresiones lambda :

#include <cassert>
int main()
{
    struct S { int p{6}, q{7}; };
    const auto& [b, d] = S{};
    auto l = [b, d] { return b * d; }; // válido desde C++20
    assert(l() == 42);
}
(hasta C++20)


Se permite que el tamaño de un enlace estructurado sea 0 siempre que la lista-de-identificadores-sb contenga exactamente un identificador que solo pueda introducir un paquete de enlace estructurado vacío.

auto return_empty() -> std::tuple<>;
template <class>
void test_empty()
{
    auto [] = return_empty(); // error
    auto [...args] = return_empty(); // OK, args es un paquete vacío
    auto [one, ...rest] = return_empty(); // error, el tamaño del enlace estructurado es demasiado pequeño
}
(desde C++26)
Macro de prueba de características Valor Std Característica
__cpp_structured_bindings 201606L (C++17) Enlaces estructurados
202403L (C++26) Enlaces estructurados con atributos
202406L (C++26) Declaración de enlace estructurado como condición
202411L (C++26) Los enlaces estructurados pueden introducir un paquete

Palabras clave

auto

Ejemplo

#include <iomanip>
#include <iostream>
#include <set>
#include <string>
int main()
{
    std::set<std::string> myset{"hello"};
    for (int i{2}; i; --i)
    {
        if (auto [iter, success] = myset.insert("Hello"); success) 
            std::cout << "La inserción es exitosa. El valor es "
                      << std::quoted(*iter) << ".\n";
        else
            std::cout << "El valor " << std::quoted(*iter)
                      << " ya existe en el conjunto.\n";
    }
    struct BitFields
    {
        // C++20: inicializador predeterminado de miembro para campos de bits
        int b : 4 {1}, d : 4 {2}, p : 4 {3}, q : 4 {4};
    };
    {
        const auto [b, d, p, q] = BitFields{};
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
    {
        const auto [b, d, p, q] = []{ return BitFields{4, 3, 2, 1}; }();
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
    }
    {
        BitFields s;
        auto& [b, d, p, q] = s;
        std::cout << b << ' ' << d << ' ' << p << ' ' << q << '\n';
        b = 4, d = 3, p = 2, q = 1;
        std::cout << s.b << ' ' << s.d << ' ' << s.p << ' ' << s.q << '\n';
    }
}

Salida:

La inserción es exitosa. El valor es "Hello".
El valor "Hello" ya existe en el conjunto.
1 2 3 4
4 3 2 1
1 2 3 4
4 3 2 1

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 2285 C++17 expression podría referirse a los nombres de identifier-list la declaración es
incorrecta en este caso
CWG 2312 C++17 el significado de mutable se perdía en el caso 3 su significado se mantiene
CWG 2313 C++17 en el caso 2, las variables de enlace estructurado podían redeclararse no pueden redeclararse
CWG 2339 C++17 en el caso 2, faltaba la definición de I se añadió la definición
CWG 2341
( P1091R3 )
C++17 los enlaces estructurados no podían
declararse con duración de almacenamiento estático
permitido
CWG 2386 C++17 el protocolo de enlace "tipo-tupla" se usaba
siempre que std:: tuple_size < E > fuera un tipo completo
se usa solo cuando std:: tuple_size < E >
tiene un miembro value
CWG 2506 C++17 si expression es de tipo array con calificación cv,
la calificación cv se transfería a E
descarta esa calificación cv
CWG 2635 C++20 los enlaces estructurados podían estar restringidos prohibido
CWG 2867 C++17 el orden de inicialización no estaba claro se aclaró
P0961R1 C++17 en el caso 2, se usaba el miembro get
si la búsqueda encontraba cualquier tipo de get
solo si la búsqueda encuentra una plantilla
de función con un parámetro constante
P0969R0 C++17 en el caso 3, se requería que los miembros fueran públicos solo se requiere que sean accesibles
en el contexto de la declaración

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 9.6 Declaraciones de enlace estructurado [dcl.struct.bind] (p: 228-229)
  • Estándar C++20 (ISO/IEC 14882:2020):
  • 9.6 Declaraciones de enlace estructurado [dcl.struct.bind] (p: 219-220)
  • Estándar C++17 (ISO/IEC 14882:2017):
  • 11.5 Declaraciones de enlace estructurado [dcl.struct.bind] (p: 219-220)

Véase también

(C++11)
crea un tuple de referencias a lvalue o desempaqueta un tuple en objetos individuales
(plantilla de función)