Direct-initialization
Inicializa un objeto a partir de un conjunto explícito de argumentos del constructor.
Contenidos |
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:
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)); // OK: initializes a[0] with A(1) and a[1] with A() A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1] // from {} selected explicit constructor |
(desde C++20) |
-
Si
Tes un tipo de clase,
|
(desde C++17) |
-
-
se examinan los constructores de
Ty se selecciona la mejor coincidencia mediante resolución de sobrecarga. Luego 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()}; // OK, lifetime is extended B b2(1, f()); // well-formed, but dangling reference B b3{1.0, 1}; // error: narrowing conversion B b4(1.0, 1); // well-formed, but dangling reference B b5(1.0, std::move(n)); // OK |
(desde C++20) |
-
De lo contrario, si
Tes un tipo no-clase pero el tipo fuente es un tipo clase, se examinan las funciones de conversión del tipo fuente y sus clases base, si las hay, y se selecciona la mejor coincidencia mediante resolución de sobrecarga. La conversión definida por el usuario seleccionada se utiliza luego para convertir la expresión inicializadora en el objeto que se está inicializando. -
De lo contrario, si
Tes bool y el tipo fuente es std::nullptr_t , el valor del objeto inicializado es false . -
De lo contrario,
conversiones estándar
se utilizan, si es necesario, para convertir el valor de
other
a la versión sin calificadores cv de
T, y el valor inicial del objeto que se está inicializando es el valor (posiblemente convertido).
Notas
La inicialización directa es más permisiva que la inicialización por copia: la inicialización por copia solo considera constructores no- explicit y funciones de conversión definidas por el usuario no explícitas conversion functions , 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 usando la sintaxis de inicialización directa (1) (con paréntesis redondos) y una declaración de función , el compilador siempre elige la declaración de función. Esta regla de desambiguación a veces es contraintuitiva y ha sido llamada la análisis más exasperante .
#include <fstream> #include <iterator> #include <string> int main() { std::ifstream file("data.txt"); // Lo siguiente es una declaración de función: std::string foo1(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // Declara una función llamada foo1, 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 al tipo puntero a función std::istreambuf_iterator<char>(*)() // Solución pre-C++11 (para declarar una variable) - agregar paréntesis adicionales alrededor de uno // de los argumentos: std::string str1((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // Solución post-C++11 (para declarar una variable) - usar inicialización de lista para cualquier // de los argumentos: std::string str2(std::istreambuf_iterator<char>{file}, {}); }
Ejemplo
#include <iostream> #include <memory> #include <string> struct Foo { int mem; explicit Foo(int n) : mem(n) {} }; int main() { std::string s1("test"); // constructor desde const char* std::string s2(10, 'a'); std::unique_ptr<int> p(new int(1)); // OK: se permiten constructores explícitos // std::unique_ptr<int> p = new int(1); // error: el constructor es explícito Foo f(2); // f se inicializa directamente: // el parámetro del constructor n se inicializa por copia desde el rvalue 2 // f.mem se inicializa directamente desde el parámetro n // Foo f2 = 2; // error: el constructor es explícito std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem << '\n'; }
Salida:
test aaaaaaaaaa 1 2