Default-initialization
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 :
Les effets de l'initialisation par défaut sont :
-
si
Test 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
Test un type tableau, chaque élément du tableau est initialisé par défaut ; - sinon, aucune initialisation n'est effectuée (voir les notes ).
|
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) |
|
(jusqu'à C++11) |
|
(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 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.
|
(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.
|
(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 :
-
- Le deuxième ou troisième opérande d'une expression conditionnelle .
- L'opérande droit d'une expression virgule .
-
L'opérande d'une
conversion intégrale
,
conversion explicite
ou
static_castvers un type compatible avec l'initialisation. - Une expression à valeur ignorée .
- 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.
| (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 |