Value-initialization
Ceci est l'initialisation effectuée lorsqu'un objet est construit avec un initialiseur vide.
Table des matières |
Syntaxe
T
()
|
(1) | ||||||||
new
T
()
|
(2) | ||||||||
Class
::
Class
(
...
)
:
member
()
{
...
}
|
(3) | ||||||||
T
object
{};
|
(4) | (depuis C++11) | |||||||
T
{}
|
(5) | (depuis C++11) | |||||||
new
T
{}
|
(6) | (depuis C++11) | |||||||
Class
::
Class
(
...
)
:
member
{}
{
...
}
|
(7) | (depuis C++11) | |||||||
Explication
L'initialisation par valeur est effectuée dans ces situations :
Dans tous les cas, si la paire d'accolades vide
{}
est utilisée et que
T
est un type agrégé,
l'initialisation agrégée
est effectuée au lieu de l'initialisation par valeur.
|
Si
|
(depuis C++11) |
Les effets de l'initialisation par valeur sont :
-
Si
Test un type classe (éventuellement qualifié cv) :
-
-
Si l'initialisation par défaut pour
Tsélectionne un constructeur , et que le constructeur n'est pas déclaré par l'utilisateur (jusqu'à C++11) fourni par l'utilisateur (depuis C++11) , l'objet est d'abord initialisé à zéro . - Dans tous les cas, l'objet est initialisé par défaut .
-
Si l'initialisation par défaut pour
-
Sinon, si
Test un type tableau, chaque élément du tableau est initialisé par valeur. - Sinon, l'objet est initialisé à zéro.
Notes
La syntaxe
T object
(
)
;
n'initialise pas un objet ; elle déclare une fonction qui ne prend aucun argument et retourne
T
. La manière de value-initialize une variable nommée avant C++11 était
T object
=
T
(
)
;
, ce qui value-initialize un temporaire puis copy-initialize l'objet : la plupart des compilateurs
optimisent la copie
dans ce cas.
Les références ne peuvent pas être initialisées par valeur.
Comme décrit dans la
conversion de type fonction
, la syntaxe
T
(
)
(1)
est interdite si
T
désigne un type tableau, tandis que
T
{
}
(5)
est autorisée.
Tous les conteneurs standards (
std::vector
,
std::list
, etc.) initialisent par valeur leurs éléments lorsqu'ils sont construits avec un seul argument
size_type
ou lorsqu'ils sont agrandis par un appel à
resize
(
)
, sauf si leur allocateur personnalise le comportement de
construct
.
Exemple
#include <cassert> #include <iostream> #include <string> #include <vector> struct T1 { int mem1; std::string mem2; virtual void foo() {} // assure que T1 n'est pas un agrégat }; // constructeur par défaut implicite struct T2 { int mem1; std::string mem2; T2(const T2&) {} // constructeur de copie fourni par l'utilisateur }; // pas de constructeur par défaut struct T3 { int mem1; std::string mem2; T3() {} // constructeur par défaut fourni par l'utilisateur }; std::string s{}; // classe => initialisation par défaut, la valeur est "" int main() { int n{}; // scalaire => initialisation à zéro, la valeur est 0 assert(n == 0); double f = double(); // scalaire => initialisation à zéro, la valeur est 0.0 assert(f == 0.0); int* a = new int[10](); // tableau => initialisation par valeur de chaque élément assert(a[9] == 0); // la valeur de chaque élément est 0 T1 t1{}; // classe avec constructeur par défaut implicite => assert(t1.mem1 == 0); // t1.mem1 est initialisé à zéro, la valeur est 0 assert(t1.mem2 == ""); // t1.mem2 est initialisé par défaut, la valeur est "" // T2 t2{}; // erreur : classe sans constructeur par défaut T3 t3{}; // classe avec constructeur par défaut fourni par l'utilisateur => std::cout << t3.mem1; // t3.mem1 est initialisé par défaut à une valeur indéterminée assert(t3.mem2 == ""); // t3.mem2 est initialisé par défaut, la valeur est "" std::vector<int> v(3); // initialisation par valeur de chaque élément assert(v[2] == 0); // la valeur de chaque élément est 0 std::cout << '\n'; delete[] a; }
Sortie possible :
42
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 publié | Comportement corrigé |
|---|---|---|---|
| CWG 178 | C++98 |
il n'y avait pas d'initialisation par valeur ; un 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 543 | C++98 |
l'initialisation par valeur pour un objet de classe sans aucun
constructeur fourni par l'utilisateur était équivalente à l'initialisation par valeur de chaque sous-objet (ce qui ne nécessitait pas d'initialiser à zéro un membre avec un constructeur par défaut fourni par l'utilisateur) |
initialise à zéro
l'objet entier, puis appelle le constructeur par défaut |
| CWG 1301 | C++11 |
l'initialisation par valeur des unions avec des constructeurs
par défaut supprimés conduisait à une initialisation à zéro |
elles sont
initialisées par défaut |
| CWG 1368 | C++98 |
tout constructeur fourni par l'utilisateur entraînait
l'omission de l'initialisation à zéro |
seul un constructeur par défaut
fourni par l'utilisateur omet l'initialisation à zéro |
| CWG 1502 | C++11 |
l'initialisation par valeur d'une union sans constructeur par défaut
fourni par l'utilisateur n'initialisait que l'objet à zéro, malgré les initialiseurs de membres par défaut |
effectue l'initialisation
par défaut après l'initialisation à zéro |
| CWG 1507 | C++98 |
l'initialisation par valeur pour un objet de classe sans aucun
constructeur fourni par l'utilisateur ne vérifiait pas la validité du constructeur par défaut lorsque ce dernier est trivial |
la validité du constructeur
par défaut trivial est vérifiée |
| CWG 2820 | C++98 |
l'initialisation par défaut suivant l'initialisation à zéro
nécessitait un constructeur non trivial |
non requis |
| CWG 2859 | C++98 |
l'initialisation par valeur pour un objet de classe pouvait impliquer
une initialisation à zéro même si l'initialisation par défaut ne sélectionnait pas réellement un constructeur fourni par l'utilisateur |
il n'y a pas
d'initialisation à zéro dans ce cas |