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

Ceci est l'initialisation effectuée lorsqu'un objet est construit sans initialiseur.

Table des matières

Syntaxe

T object  ; (1)
new T (2)

Explication

L'initialisation par défaut est effectuée dans trois situations :

1) lorsqu'une variable avec une durée de stockage automatique, statique ou locale au thread est déclarée sans initialiseur ;
2) lorsqu'un objet avec une durée de stockage dynamique est créé par une new-expression sans initialiseur ;
3) lorsqu'une classe de base ou un membre de données non statique n'est pas mentionné dans une liste d'initialisation du constructeur et que ce constructeur est appelé.

Les effets de l'initialisation par défaut sont :

  • si T est un type classe (éventuellement qualifié cv) non-POD (jusqu'à C++11) , les constructeurs sont considérés et soumis à la résolution de surcharge avec une liste d'arguments vide. Le constructeur sélectionné (qui est l'un des constructeurs par défaut ) est appelé pour fournir la valeur initiale du nouvel objet ;
  • si T est un type tableau, chaque élément du tableau est initialisé par défaut ;
  • sinon, aucune initialisation n'est effectuée (voir les notes ).

Initialisation par défaut d'un objet const

Si un programme nécessite l'initialisation par défaut d'un objet d'un type const -qualifié T , T doit être un type classe const-default-constructible ou un tableau de celui-ci.

Un type de classe T est const-default-constructible si l'initialisation par défaut de T invoquerait un constructeur fourni par l'utilisateur de T (non hérité d'une classe de base) (depuis C++11) ou si

Seuls les types de classe non-POD (éventuellement qualifiés cv) (ou leurs tableaux) avec une durée de stockage automatique étaient considérés comme initialisés par défaut lorsqu'aucun initialiseur n'est utilisé. Les types scalaires et POD avec une durée de stockage dynamique étaient considérés comme non initialisés (depuis C++11, cette situation a été reclassifiée comme une forme d'initialisation par défaut).

(until C++11)
  • chaque membre de données non statique direct M de T est de type classe X (ou tableau de celui-ci), X est const-default-constructible, et
  • T n'a pas de membres variant directs, et
(jusqu'à C++11)
  • chaque membre de données non statique non variant direct M de T a un initialiseur de membre par défaut ou, si M est de type classe X (ou tableau de celui-ci), X est const-default-constructible,
  • si T est une union avec au moins un membre de données non statique, exactement un membre variant a un initialiseur de membre par défaut,
  • si T n'est pas une union, pour chaque membre union anonyme avec au moins un membre de données non statique (le cas échéant), exactement un membre de données non statique a un initialiseur de membre par défaut, et
(depuis C++11)

chaque sous-objet potentiellement construit de la classe de base de T est const-default-constructible.

Valeurs indéterminées et erronées

Lorsque le stockage pour un objet avec une durée de stockage automatique ou dynamique est obtenu, l'objet a une valeur indéterminée .

Si aucune initialisation n'est effectuée pour un objet, cet objet conserve une valeur indéterminée jusqu'à ce que cette valeur soit remplacée.

(jusqu'en C++26)

Lorsque le stockage pour un objet avec une durée de stockage automatique ou dynamique est obtenu, les octets composant le stockage de l'objet ont la valeur initiale suivante :

  • Si l'objet a une durée de stockage dynamique, ou est l'objet associé à une variable ou paramètre de fonction dont la première déclaration est marquée avec [[ indeterminate ]] , les octets ont des valeurs indéterminées .
  • Sinon, les octets ont des valeurs erronées , où chaque valeur est déterminée par l'implémentation indépendamment de l'état du programme.

Si aucune initialisation n'est effectuée pour un objet (y compris les sous-objets ), un tel octet conserve sa valeur initiale jusqu'à ce que cette valeur soit remplacée.

  • Si un bit quelconque dans la représentation de la valeur a une valeur indéterminée, l'objet a une valeur indéterminée .
  • Sinon, si un bit quelconque dans la représentation de la valeur a une valeur erronée, l'objet a une valeur erronée .
(depuis C++26)

Si une évaluation produit une valeur indéterminée, le comportement est undefined .

Si une évaluation produit une valeur erronée, le comportement est erroné .

(since C++26)

Cas particuliers

Les types suivants sont adaptés à l'absence d'initialisation :

(depuis C++17)
  • unsigned char
  • char , si son type sous-jacent est unsigned char

Étant donné une valeur indéterminée ou erronée (depuis C++26) value , la valeur résultat non initialisée de value est :

  • Une valeur indéterminée, si value est également une valeur indéterminée.
  • value , si value est une valeur erronée.
(depuis C++26)

Si une évaluation eval produit une valeur indéterminée ou erronée (depuis C++26) d'un type compatible avec l'initialisation value , le comportement est bien défini dans les cas suivants :

  • eval est l'évaluation d'une des expressions et opérandes suivantes :
Dans ce cas, le résultat de l'opération est la valeur non initialisée de value .
  • eval est une évaluation de l'opérande droit d'un opérateur d'affectation simple dont l'opérande gauche est une lvalue d'un type compatible avec l'initialisation.
Dans ce cas, la valeur de l'objet référencé par l'opérande gauche est remplacée par la valeur de résultat non initialisée de value .
  • eval est l'évaluation de l'expression d'initialisation lors de l'initialisation d'un objet d'un type adapté à l'initialisation.
  • value ne peut pas être de type std::byte si l'objet en cours d'initialisation n'est pas de type std::byte .
(depuis C++17)
Dans ce cas, cet objet est initialisé avec la valeur de résultat non initialisée de value .

Convertir une valeur indéterminée d'un type tolérant l'initialisation produit une valeur indéterminée.

La conversion d'une valeur erronée d'un type tolérant l'initialisation produit une valeur erronée, le résultat de la conversion est la valeur de l'opérande converti.

(depuis C++26)
// Cas 1 : Objets non initialisés avec durée de stockage dynamique
// Toutes les versions C++ : valeur indéterminée + comportement indéfini
int f(bool b)
{
    unsigned char* c = new unsigned char;
    unsigned char d = *c; // OK, « d » a une valeur indéterminée
    int e = d;            // comportement indéfini
    return b ? d : 0;     // comportement indéfini si « b » est vrai
}
// Cas 2 : Objets non initialisés avec durée de stockage automatique
// jusqu'à C++26 : valeur indéterminée + comportement indéfini
// depuis C++26 : valeur erronée + comportement erroné
int g(bool b)
{
    unsigned char c;     // « c » a une valeur indéterminée/erronée
    unsigned char d = c; // pas de comportement indéfini/erroné,
                         // mais « d » a une valeur indéterminée/erronée
    assert(c == d);      // tient, mais les deux promotions entières ont
                         // un comportement indéfini/erroné
    int e = d;           // comportement indéfini/erroné
    return b ? d : 0;    // comportement indéfini/erroné si « b » est vrai
}
// Identique au cas 2
void h()
{
    int d1, d2;  // « d1 » et « d2 » ont des valeurs indéterminées/erronées
    int e1 = d1; // comportement indéfini/erroné
    int e2 = d1; // comportement indéfini/erroné
    assert(e1 == e2); // tient
    assert(e1 == d1); // tient, comportement indéfini/erroné
    assert(e2 == d1); // tient, comportement indéfini/erroné
    // pas de comportement indéfini/erroné,
    // mais « d2 » a une valeur indéterminée/erronée
    std::memcpy(&d2, &d1, sizeof(int));
    assert(e1 == d2); // tient, comportement indéfini/erroné
    assert(e2 == d2); // tient, comportement indéfini/erroné
}

Notes

Les références et les objets scalaires const ne peuvent pas être initialisés par défaut.

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_constexpr 201907L (C++20) Initialisation par défaut triviale et déclaration asm dans les fonctions constexpr

Exemple

#include <string>
struct T1 { int mem; };
struct T2
{
    int mem;
    T2() {} // "mem" n'est pas dans la liste d'initialisation
};
int n; // static non-class, une initialisation en deux phases est effectuée :
       // 1) l'initialisation à zéro initialise n à zéro
       // 2) l'initialisation par défaut ne fait rien, laissant n à zéro
int main()
{
    [[maybe_unused]]
    int n;            // non-class, la valeur est indéterminée
    std::string s;    // class, appelle le constructeur par défaut, la valeur est ""
    std::string a[2]; // array, initialise par défaut les éléments, la valeur est {"", ""}
//  int& r;           // Erreur : une référence
//  const int n;      // Erreur : un const non-class
//  const T1 t1;      // Erreur : const class avec constructeur par défaut implicite
    [[maybe_unused]]
    T1 t1;            // class, appelle le constructeur par défaut implicite
    const T2 t2;      // const class, appelle le constructeur par défaut fourni par l'utilisateur
                      // t2.mem est initialisé par défaut
}

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement tel que publié Comportement correct
CWG 178 C++98 il n'y avait pas d'initialisation par valeur ;
l'initialiseur vide invoquait l'initialisation par défaut
(bien que new T ( ) effectue également une initialisation à zéro)
l'initialiseur vide invoque
l'initialisation par valeur
CWG 253 C++98 l'initialisation par défaut d'un objet const ne pouvait pas
appeler un constructeur par défaut implicitement déclaré
autorisée si tous les sous-objets sont initialisés
CWG 616 C++98 la conversion lvalue en rvalue de tout
objet non initialisé était toujours UB
l' unsigned char indéterminé est autorisé
CWG 1787 C++98 la lecture d'un unsigned char indéterminé
mis en cache dans un registre était UB
rendue bien définie

Voir aussi