Namespaces
Variants

Default-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

Esta es la inicialización realizada cuando un objeto se construye sin inicializador.

Contenidos

Sintaxis

T objeto  ; (1)
new T (2)

Explicación

La inicialización por defecto se realiza en tres situaciones:

1) cuando una variable con duración de almacenamiento automática, estática o local a hilo storage duration se declara sin inicializador;
2) cuando un objeto con duración de almacenamiento dinámico es creado por una new-expression sin inicializador;
3) cuando una clase base o un miembro de datos no estático no se menciona en una lista de inicialización del constructor y se llama a ese constructor.

Los efectos de la inicialización por defecto son:

  • si T es un tipo clase (posiblemente calificado cv) non-POD (until C++11) , se consideran los constructores y se someten a overload resolution contra la lista de argumentos vacía. El constructor seleccionado (que es uno de los default constructors ) se llama para proporcionar el valor inicial para el nuevo objeto;
  • si T es un tipo array, cada elemento del array es default-initialized;
  • de lo contrario, no se realiza ninguna inicialización (ver notes ).

Inicialización por defecto de un objeto const

Si un programa requiere la inicialización por defecto de un objeto de un tipo calificado como const T , T deberá ser un tipo de clase const-default-constructible o un arreglo del mismo.

Un tipo de clase T es const-default-constructible si la inicialización por defecto de T invocaría un constructor proporcionado por el usuario de T (no heredado de una clase base) (desde C++11) o si

Solo los tipos de clase no-POD (posiblemente calificados cv) (o arreglos de estos) con duración de almacenamiento automático se consideraban inicializados por defecto cuando no se usaba un inicializador. Los tipos escalares y POD con duración de almacenamiento dinámico se consideraban no inicializados (desde C++11, esta situación se reclasificó como una forma de inicialización por defecto).

(until C++11)
  • cada miembro de datos no estático directo M de T es de tipo clase X (o arreglo del mismo), X es const-default-constructible, y
  • T no tiene miembros variantes directos, y
(hasta C++11)
  • cada miembro de datos no estático no variante directo M de T tiene un inicializador de miembro predeterminado o, si M es de tipo clase X (o arreglo del mismo), X es const-default-constructible,
  • si T es una unión con al menos un miembro de datos no estático, exactamente un miembro variante tiene un inicializador de miembro predeterminado,
  • si T no es una unión, para cada miembro de unión anónima con al menos un miembro de datos no estático (si existe), exactamente un miembro de datos no estático tiene un inicializador de miembro predeterminado, y
(desde C++11)

cada subobjeto potencialmente construido de la clase base de T es const-default-constructible.

Valores indeterminados y erróneos

Cuando se obtiene almacenamiento para un objeto con duración de almacenamiento automática o dinámica, el objeto tiene un valor indeterminado .

Si no se realiza inicialización para un objeto, ese objeto retiene un valor indeterminado hasta que ese valor es reemplazado.

(until C++26)

Cuando se obtiene almacenamiento para un objeto con duración de almacenamiento automática o dinámica, los bytes que componen el almacenamiento para el objeto tienen el siguiente valor inicial:

  • Si el objeto tiene duración de almacenamiento dinámica, o es el objeto asociado con una variable o parámetro de función cuya primera declaración está marcada con [[ indeterminate ]] , los bytes tienen valores indeterminados .
  • En caso contrario, los bytes tienen valores erróneos , donde cada valor es determinado por la implementación independientemente del estado del programa.

Si no se realiza inicialización para un objeto (incluyendo subobjetos ), dicho byte retiene su valor inicial hasta que ese valor es reemplazado.

  • Si cualquier bit en la representación de valor tiene un valor indeterminado, el objeto tiene un valor indeterminado .
  • En caso contrario, si cualquier bit en la representación de valor tiene un valor erróneo, el objeto tiene un valor erróneo .
(since C++26)

Si una evaluación produce un valor indeterminado, el comportamiento es undefined .

Si una evaluación produce un valor erróneo, el comportamiento es erróneo .

(since C++26)

Casos especiales

Los siguientes tipos son uninitialized-friendly :

(desde C++17)
  • unsigned char
  • char , si su tipo subyacente es unsigned char

Dado un valor indeterminado o erróneo (desde C++26) , value , el valor de resultado no inicializado de value es:

  • Un valor indeterminado, si value también es un valor indeterminado.
  • value , si value es un valor erróneo.
(desde C++26)

Si una evaluación eval produce un valor indeterminado o erróneo (desde C++26) de un tipo uninitialized-friendly, el comportamiento está bien definido en los siguientes casos:

  • eval es la evaluación de una de las siguientes expresiones y operandos:
En este caso, el resultado de la operación es el valor de resultado no inicializado de value .
En este caso, el valor del objeto referido por el operando izquierdo es reemplazado por el valor resultante no inicializado de value .
  • eval es la evaluación de la expresión de inicialización al inicializar un objeto de un tipo amigable con la no inicialización.
(desde C++17)
En este caso, ese objeto se inicializa al valor de resultado no inicializado de value .

Convertir un valor indeterminado de un tipo amigable a la no inicialización produce un valor indeterminado.

Convertir un valor erróneo de un tipo amigable a la no inicialización produce un valor erróneo, el resultado de la conversión es el valor del operando convertido.

(since C++26)
// Caso 1: Objetos no inicializados con duración de almacenamiento dinámico
// Todas las versiones de C++: valor indeterminado + comportamiento indefinido
int f(bool b)
{
    unsigned char* c = new unsigned char;
    unsigned char d = *c; // OK, "d" tiene un valor indeterminado
    int e = d;            // comportamiento indefinido
    return b ? d : 0;     // comportamiento indefinido si "b" es verdadero
}
// Caso 2: Objetos no inicializados con duración de almacenamiento automático
// hasta C++26: valor indeterminado + comportamiento indefinido
// desde C++26: valor erróneo + comportamiento erróneo
int g(bool b)
{
    unsigned char c;     // "c" tiene un valor indeterminado/erróneo
    unsigned char d = c; // sin comportamiento indefinido/erróneo,
                         // pero "d" tiene un valor indeterminado/erróneo
    assert(c == d);      // se cumple, pero ambas promociones integrales tienen
                         // comportamiento indefinido/erróneo
    int e = d;           // comportamiento indefinido/erróneo
    return b ? d : 0;    // comportamiento indefinido/erróneo si "b" es verdadero
}
// Igual que el caso 2
void h()
{
    int d1, d2;  // "d1" y "d2" tienen valores indeterminados/erróneos
    int e1 = d1; // comportamiento indefinido/erróneo
    int e2 = d1; // comportamiento indefinido/erróneo
    assert(e1 == e2); // se cumple
    assert(e1 == d1); // se cumple, comportamiento indefinido/erróneo
    assert(e2 == d1); // se cumple, comportamiento indefinido/erróneo
    // sin comportamiento indefinido/erróneo,
    // pero "d2" tiene un valor indeterminado/erróneo
    std::memcpy(&d2, &d1, sizeof(int));
    assert(e1 == d2); // se cumple, comportamiento indefinido/erróneo
    assert(e2 == d2); // se cumple, comportamiento indefinido/erróneo
}

Notas

Las referencias y los objetos escalares const no pueden inicializarse por defecto.

Macro de prueba de características Valor Std Característica
__cpp_constexpr 201907L (C++20) Inicialización por defecto trivial y declaración asm en funciones constexpr

Ejemplo

#include <string>
struct T1 { int mem; };
struct T2
{
    int mem;
    T2() {} // “mem” no está en la lista de inicialización
};
int n; // estático no-clase, se realiza una inicialización en dos fases:
       // 1) inicialización a cero inicializa n a cero
       // 2) inicialización por defecto no hace nada, dejando n en cero
int main()
{
    [[maybe_unused]]
    int n;            // no-clase, el valor es indeterminado
    std::string s;    // clase, llama al constructor por defecto, el valor es ""
    std::string a[2]; // array, inicializa por defecto los elementos, el valor es {"", ""}
//  int& r;           // Error: una referencia
//  const int n;      // Error: un const no-clase
//  const T1 t1;      // Error: clase const con constructor por defecto implícito
    [[maybe_unused]]
    T1 t1;            // clase, llama al constructor por defecto implícito
    const T2 t2;      // clase const, llama al constructor por defecto proporcionado por el usuario
                      // t2.mem se inicializa por defecto
}

Informes de defectos

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

DR Se aplica a Comportamiento publicado Comportamiento correcto
CWG 178 C++98 no existía la inicialización por valor;
el inicializador vacío invocaba la inicialización por defecto
(aunque new T ( ) también realiza inicialización a cero)
el inicializador vacío invoca
inicialización por valor
CWG 253 C++98 la inicialización por defecto de un objeto const no podía
llamar a un constructor por defecto declarado implícitamente
permitido si todos los subobjetos están inicializados
CWG 616 C++98 la conversión de lvalue a rvalue de cualquier
objeto no inicializado siempre era UB
se permite unsigned char indeterminado
CWG 1787 C++98 la lectura de un unsigned char indeterminado
almacenado en caché en un registro era UB
se define correctamente

Véase también