Espacios de nombres
Variantes
Acciones

Inicialización de lista (desde C++11)

De cppreference.com
< cpp‎ | language

Inicializa un objeto a partir de una lista de inicializadores entre llaves.

Contenido

[editar] Sintaxis

[editar] Inicialización de lista directa

T objeto { arg1, arg2, ... }; (1)
T { arg1, arg2, ... } (2)
new T { arg1, arg2, ... } (3)
Clase { T miembro { arg1, arg2, ... }; }; (4)
Clase::Clase() : miembro{arg1, arg2, ...} {... (5)

[editar] Inicialización de lista de copia

T objeto = {arg1, arg2, ...}; (6)
función( { arg1, arg2, ... } ) (7)
return { arg1, arg2, ... } ; (8)
objeto[ { arg1, arg2, ... } ] (9)
objeto = { arg1, arg2, ... } (10)
U( { arg1, arg2, ... } ) (11)
Clase { T miembro = { arg1, arg2, ... }; }; (12)

La inicialización de lista se lleva a cabo en las siguientes situaciones:

  • Inicialización de lista directa (se consideran los constructores explícitos como los no explícitos):
1) la inicialización de una variable denominada con una lista de inicializadores entre llaves (es decir, una lista de expresiones entre llaves, posiblemente vacía, o listas de inicializadores entre llaves anidadas);
2) la inicialización de un temporal no denominado con una lista de inicializadores entre llaves;
3) la inicialización de un objeto cuya duración de almacenamiento dinámico con una expresión new, donde el inicializador es una lista de inicializadores entre llaves;
4) en un inicializador de dato miembro no estático que no usa el signo igual;
5) en una lista de inicializadores de miembros de un constructor si se usa una lista de inicializadores entre llaves.
  • Inicialización de lista de copia (se consideran tanto los constructores tanto explícitos como no explícitos, pero solamente puede llamarse a los constructores no explícitos):
6) la inicialización de una variable denominada con una lista de inicializadores entre llaves después del signo igual;
7) en una expresión de llamada a función, con una lista de inicializadores entre llaves usada como un argumento, y la inicialización de lista inicializa el parámetro de función;
8) en una instrucción return con una lista de inicializadores entre llaves usada como la expresión de retorno y la inicialización de lista inicializa el objeto devuelto;
9) en una expresión subíndice con un operador operator[] definido por el usuario, donde la inicialización de lista inicializa el parámetro del operador sobrecargado;
10) en una expresión de asignación, donde la inicialización de lista inicializa el parámetro del operador sobrecargado;
11) en una expresión de conversión funcional u otras invocaciones al constructor, donde la lista de inicializadores entre llaves es usada en lugar de un argumento para el constructor. La inicialización de lista de copia inicializa el parámetro del constructor (observa que el tipo U en este ejemplo no es el tipo que se está inicializando mediante la inicialización de lista, sino el parámetro del constructor de U);
12) en un inicializador de dato miembro no estático que usa el signo igual.

[editar] Explicación

Los efectos de la inicialización de lista de un objeto de tipo T son:

  • Si T es una clase agregado y la lista de inicializadores tiene un solo elemento del mimso o de un tipo derivado (posiblemente calificado-cv), el objeto se inicializa a partir de ese elemento (mediante la inicialización de copia para la inicialización de list de copia, o mediante la inicialización directa para la inicialización de lista directa).
  • De lo contrario, si T es un array de caracteres y la lista de inicializadores tiene un solo elemento que es un literal de cadena del tipo apropriado, el array se inicializa del literal de cadena, como es habitual.
(desde C++14)
(hasta C++14)
  • De lo contrario, si la lista de inicializadores entre llaves está vacía y T es un tipo clase con un constructor por defecto, se realiza la inicialización de un valor.
(desde C++14)
  • De lo contrario, si T es una especialización de std::initializer_list, el objeto T se inicializa mediante la inicialización directa o la inicialización de copia, dependiendo del contexto, a partir de un prvalue del mismo tipo inicializado a partir de (hasta C++17) la lista de inicializadores entre llaves.
  • De lo contrario, se consideran los constructores de T en dos fases:
  • Si la etapa previa no produce una coincidencia, todos los constructores de T participan en la resolución de sobrecarga frente al conjunto de argumentos que consiste en los elementos de la lista de inicializadores entre llaves, con la restricción que solamente se permiten las conversiones no estrechantes. Si esta etapa produce un constructor explícito como la mejor coincidencia para una inicialización de lista de copia, la compilación falla (observa que en una simple inicialización de copia, los constructores explícitos no se consideran para nada).
  • De lo contrario, si T es un tipo enumeración que es ya sea con ámbito o sin ámbito con un tipo subyacente fijo, y si la lista de inicializadores entre llaves solamente tiene un inicializador, y si la conversión a partir del inicializador al tipo subyacente no es estrechante, y si la inicialización es la inicialización de lista directa, entonces la enumeración se inicializa con el resultado de convertir el inicializador a su tipo subyacente.
(desde C++17)
  • De lo contrario (si T no es un tipo clase), si la lista de inicializadores entre llaves solamente tiene un elemento y ya sea que T no es un tipo referencia o es un tipo referencia cuyo tipo referenciado es el mismo que, o es una clase base del tipo del elemento, T se inicializa mediante la inicialización directa (en la inicialización de lista directa) o la inicialización de copia (en la inicialización de lista de copia), excepto que no se permiten las conversiones estrechantes.
  • De lo contrario, si T es un tipo referencia que no es compatible con el tipo del elemento, se inicializa un temporal del tipo referenciado o su tipo de array correspondiente de límite conocido (desde C++20) mediante la inicialización de lista, y la referencia se vincula al temporal (esto falla si la referencia es una referencia lvalue no const).
Si el tipo referenciado es un tipo array de límite desconocido U[], el tipo del temporal es U[N], como si se hubiera definido como U temp[]/*lista-de-inic*/;, donde /*lista-de-inic*/ es la lista de inicializadores.
(desde C++20)
  • De lo contrario, si la lista de inicializadores entre llaves no tiene elementos, T se inicializa mediante la inicialización de un valor.

[editar] Conversiones de estrechamiento

La inicialización de lista limita las conversiones implícitas permitidas prohibiendo lo siguiente:

  • la conversión de un tipo de punto flotante a un tipo entero;
  • la conversión de un long double a double o a float y la conversión de double a float, excepto donde la fuente es una expresión constante y no ocurre desbordamiento;
  • la conversión de un tipo entero a un tipo de punto flotante, excepto donde la fuente es una expresión constante cuyo valor puede almacenarse exactamente en el tipo destino;
  • la conversión de un tipo entero o de enumeración sin ámbito a un tipo entero que no puede representar todos los valores del original, excepto donde la fuente es una expresión constante cuyo valor puede almacenarse exactamente en el tipo destino;
  • la conversión de un tipo puntero o un tipo puntero a miembro a bool.
(desde C++20)

[editar] Notas

Cada cláusula de inicializador está secuenciada antes que cualquier cláusula de inicializador que la sucede en la lista de inicializadores entre llaves. Esto contrasta con los argumentos de una expresión de llamada a función, que están sin secuenciar.

Una lista de inicializadores entre llaves no es una expresión y por lo tanto no tiene tipo. Por ejemplo, decltype({1,2}) está mal formada. Que no tenga tipo implica que la deducción de tipo de plantilla no puede deducir un tipo que coincida una lista de inicializadores entre llaves, de tal manera que dada la declaración template<class T> void f(T); la expresión f({1,2,3}) está mal formada. Sin embargo, el parámetro de plantilla puede deducirse, como es el caso de std::vector<int> v(std::istream_iterator<int>(std::cin), {}), donde el tipo del iterador se deduce por el primer argumento pero también se usa en la segunda posición del parámetro. Se hace una excepción especial para deducción de tipos usando la palabra clave auto , que deduce cualquier lista de inicializadores entre llaves como std::initializer_list en la inicialización de lista de copia.

De igual manera, como la lista de inicializadores entre llaves no tiene tipo, se aplican las reglas especiales para la resolución de sobrecarga cuando se usa como un argumento para una llamada a una función sobrecargada.

Los agregados se inicializan mediante la inicialización de copia/movimiento directamente a partir de listas de inicializadores entre llaves de un solo elemento del mismo tipo, pero los no agregados primero consideran los constructores que toman una std::initializer_list:

struct X {
    X() = default;
    X(const X&) = default;
};
 
struct Q {
    Q() = default;
    Q(Q const&) = default;
    Q(std::initializer_list<Q>) {}
};
 
int main() {
  X x;
  X x2 = X { x }; // constructor de copia (no es inicialización de agregado)
  Q q;
  Q q2 = Q { q }; // constructor que toma una lista de inicializadores
                  // (no es el constructor de copia)
}
(desde C++14)

[editar] Ejemplo

#include <iostream>
#include <vector>
#include <map>
#include <string>
 
struct Foo {
    std::vector<int> mem = {1,2,3}; // inicialización de lista de un miembro no estático
    std::vector<int> mem2;
    Foo() : mem2{-1, -2, -3} {}     // inicialización de lista de un miembro en el ctor
};
 
std::pair<std::string, std::string> f(std::pair<std::string, std::string> p)
{
    return {p.second, p.first};     // inicialización de lista en la instrucción return
}
 
int main()
{
    int n0{};     // inicialización de un valor (a cero)
    int n1{1};    // inicialización de lista directa
    std::string s1{'a', 'b', 'c', 'd'}; // llamada al ctor con lista de inicializadores
    std::string s2{s1, 2, 2};           // llamada regular al ctor
    std::string s3{0x61, 'a'}; // se prefiere ctor con lista de inicializadores (int, char)
 
    int n2 = {1};           // inicialización de lista de copia
    double d = double{1.2}; // inicialización de lista de un temporal,
                            // luego inicialización de copia
 
    std::map<int, std::string> m = { // inicialización de lista anidada
           {1, "a"},
           {2, {'a', 'b', 'c'} },
           {3, s1}
    };
 
    std::cout << f({"hola", "mundo"}).first  // inicialización de lista
              << '\n';                       // en una llamada a función
 
    const int (&ar)[2] = {1,2}; // vincula referencia lvalue al array temporal
    int&& r1 = {1}; // vincula referencia rvalue al int temporal
//  int& r2 = {2};  // ERROR: no se puede vincular rvalue a ref lvalue no-const
 
//  int bad{1.0};          // ERROR: conversión de estrechamiento
    unsigned char uc1{10}; // de acuerdo
//  unsigned char uc2{-1}; // ERROR: conversión de estrechamiento
 
    Foo f;
 
    std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
              << s1 << ' ' << s2 << ' ' << s3 << '\n';
    for(auto p: m)
        std::cout << p.first << ' ' << p.second << '\n';
    for(auto n: f.mem)
        std::cout << n << ' ';
    for(auto n: f.mem2)
        std::cout << n << ' ';
}

Salida:

mundo
0 1 1
abcd cd aa
1 a
2 abc
3 abcd
1 2 3 -1 -2 -3

[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
CWG 1467 C++14 la inicialización de agregados del mismo tipo
y char arrays estaba prohibido
inicialización del mismo tipo se permite
CWG 1467 C++14 los constructores de std::initializer_list tenían prioridad
sobre los constructores de copia para listas de un solo
elemento
las listas de un solo elemento inicializan directamente

[editar] Véase también