Inicialización directa
Inicializa un objeto a partir de un conjunto explícito de argumentos del constructor.
Sintaxis
T objeto ( arg );
T objeto |
(1) | ||||||||
T objeto { arg };
|
(2) | (desde C++11) | |||||||
T ( otro )
T |
(3) | ||||||||
static_cast< T >( otro )
|
(4) | ||||||||
new T(args, ...)
|
(5) | ||||||||
Clase::Clase() : miembro(args, ...) { ... }
|
(6) | ||||||||
[arg](){ ... }
|
(7) | (desde C++11) | |||||||
Explicación
La inicialización directa se realiza en las siguientes situaciones:
new con un inicializador no vacío;Los efectos de la inicialización directa son:
- Si
Tes un tipo array,
|
(hasta C++20) |
struct A { explicit A(int i = 0) {} };
A a[2](A(1)); // de acuerdo: inicializa a a[0] con A(1) y a a[1] con A()
A b[2]{A(1)}; // ERROR: inicialización de copia y lista implícita de b[1]
// del constructor explícito {} seleccionado
|
(desde C++20) |
- Si
Tes de tipo clase,
|
(desde C++17) |
- Se examinan los constructores de
Ty se selecciona la mejor coincidencia por resolución de sobrecarga. Entonces se llama al constructor para inicializar el objeto.
- Se examinan los constructores de
struct B {
int a;
int&& r;
};
int f();
int n = 10;
B b1{1, f()}; // de acuerdo, se extiende la duración
B b2(1, f()); // bien formado, pero referencia pendiente
B b3{1.0, 1}; // ERROR: conversión de estrechamiento
B b4(1.0, 1); // bien formado, pero referencia pendiente
B b5(1.0, std::move(n)); // de acuerdo
|
(desde C++20) |
- De lo contrario, si
Tes de un tipo no clase pero el tipo fuente es un tipo clase, las funciones de conversión del tipo fuente y sus clases base, si es que las tiene, se examinan y se selecciona la mejor coincidencia por resolución de sobrecarga. La conversión definida por el usuario seleccionads se usa entonces para convertir la expresión inicializadora en el objeto que está inicializándose. - De otra forma, se usan las conversiones estándar si es necesario, para convertir el valor de otro a la versión no calificada-cv de
T, y el valor inicial del objeto que está inicializándose es el valor (posiblemente convertido).
Notas
La inicialización directa es más permisiva que la inicialización de copia: la inicialización de copia solamente considera los constructores no-explicit y las funciones de conversión definidas por el usuario no-explicit, mientras que la inicialización directa considera todos los constructores y todas las funciones de conversión definidas por el usuario.
En caso de ambigüedad entre una declaración de variable que usa la sintaxis de la inicialización directa (1) (con paréntesis redondos) y una declaración de función, el compilador siempre escoge la declaración de función. Esta regla de desambiguación es a veces contraintuitiva y se le ha llamado el análisis más irritante.
#include <iterator>
#include <string>
#include <fstream>
int main()
{
std::ifstream file("data.txt");
// lo siguiente es una declaración de función:
std::string str(std::istreambuf_iterator<char>(file),
std::istreambuf_iterator<char>());
// declara una función llamada str, cuyo tipo de retorno es std::string,
// el primer parámetro tiene tipo std::istreambuf_iterator<char> y el nombre "file"
// el segundo parámetro no tiene nombre y tiene tipo std::istreambuf_iterator<char>(),
// que se reescribe a un tipo puntero a función std::istreambuf_iterator<char>(*)()
// corrección pre-C++11: paréntesis extra circundando uno de los argumentos
std::string str( (std::istreambuf_iterator<char>(file) ),
std::istreambuf_iterator<char>());
// corrección pos-C++11 : inicialización de lista para cualquiera de los argumentos
std::string str(std::istreambuf_iterator<char>{file}, {});
}
struct M { };
struct L { L(M&); };
M n;
void f() {
M(m); // declaración, equivalente a M m;
L(n); // declaración mal formada
L(l)(m); // todavía una declaración
}
Ejemplo
#include <string>
#include <iostream>
#include <memory>
struct Foo {
int mem;
explicit Foo(int n) : mem(n) {}
};
int main()
{
std::string s1("test"); // constructor a partir de const char*
std::string s2(10, 'a');
std::unique_ptr<int> p(new int(1)); // de acuerdo: se permiten constructores explicit
// std::unique_ptr<int> p = new int(1); // ERROR: constructor es explicit
Foo f(2); // f inicializada mediante inicialización directa:
// parámetro del ctor n se inicializa mediante inicialización de copia
// a partir del rvalue 2
// f.mem se inicializa mediante inicialización directa del parámetro n
// Foo f2 = 2; // ERROR: constructor es explicit
std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem << '\n';
}
Salida:
test aaaaaaaaaa 1 2