Namespaces
Variants

Direct-initialization

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

Inicializa un objeto a partir de un conjunto explícito de argumentos del constructor.

Contenidos

Sintaxis

T objeto ( arg );

T objeto ( arg1, arg2, ... );

(1)
T objeto { arg }; (2) (desde C++11)
T ( otro )

T ( arg1, arg2, ... )

(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:

1) Inicialización con una lista de expresiones entre paréntesis no vacía o braced-init-lists (since C++11) .
2) Inicialización de un objeto de tipo no-clase con un único inicializador entre llaves (nota: para tipos clase y otros usos de braced-init-list, ver list-initialization ) (since C++11) .
3) Inicialización de un temporal prvalue (hasta C++17) el objeto resultado de un prvalue (desde C++17) mediante function-style cast o con una lista de expresiones entre paréntesis.
4) Inicialización de un temporal prvalue (hasta C++17) el objeto resultado de un prvalue (desde C++17) mediante una expresión static_cast .
5) Inicialización de un objeto con duración de almacenamiento dinámico mediante una expresión new con un inicializador.
6) Inicialización de una base o un miembro no estático mediante la lista de inicialización del constructor initializer list .
7) Inicialización de los miembros del objeto de clausura a partir de las variables capturadas por copia en una expresión lambda.

Los efectos de la inicialización directa son:

  • Si T es un tipo array,
  • el programa está mal formado.
(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 T es un tipo de clase,
  • si el inicializador es una expresión prvalue cuyo tipo es la misma clase que T (ignorando calificadores cv), la propia expresión inicializadora, en lugar de un temporal materializado a partir de ella, se utiliza para inicializar el objeto destino.
    (Antes de C++17, el compilador podía omitir la construcción desde el temporal prvalue en este caso, pero el constructor apropiado aún debía ser accesible: ver copy elision )
(desde C++17)
  • se examinan los constructores de T y se selecciona la mejor coincidencia mediante resolución de sobrecarga. Luego se llama al constructor para inicializar el objeto.
  • de lo contrario, si el tipo de destino es una clase agregada (posiblemente calificada con cv), se inicializa como se describe en inicialización de agregados excepto que se permiten conversiones de estrechamiento, no se permiten inicializadores designados, un temporal vinculado a una referencia no tiene su vida útil extendida, no hay omisión de llaves, y cualquier elemento sin un inicializador es inicializado a valor .
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 T es 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 T es 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

Véase también