Aggregate initialization
Initialise un agrégat à partir d'une liste d'initialisation . C'est une forme d' list-initialization (since C++11) .
Table des matières |
Syntaxe
T objet
= {
arg1, arg2, ...
};
|
(1) | ||||||||
T objet
{
arg1, arg2, ...
};
|
(2) | (depuis C++11) | |||||||
T objet
= { .
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(3) | (depuis C++20) | |||||||
T objet
{ .
des1
=
arg1
, .
des2
{
arg2
}
...
};
|
(4) | (depuis C++20) | |||||||
Définitions
Agrégat
Un agrégat est l'un des types suivants :
- types de tableau
- types de classe qui ont
|
(jusqu'à C++11) |
|
(depuis C++11)
(jusqu'à C++20) |
|
(depuis C++20) |
-
- aucun membre de données non statique direct privé ou protégé
|
(jusqu'en C++17) |
|
(depuis C++17) |
-
- aucune fonction membre virtuelle
|
(depuis C++11)
(jusqu'à C++14) |
Élément
Les éléments d'un agrégat sont :
- pour un tableau, les éléments du tableau dans l'ordre croissant des indices, ou
|
(jusqu'en C++17) |
|
(depuis C++17) |
Appartenance
Chaque clause d'initialisation dans une liste d'initialisation entre accolades est dite appartenir à un élément de l'agrégat en cours d'initialisation ou à un élément d'un de ses sous-agrégats.
En considérant la séquence des clauses d'initialisation, et la séquence des éléments de l'agrégat initialement formée comme la séquence des éléments de l'agrégat en cours d'initialisation et potentiellement modifiée comme décrit ci-dessous :
- Pour chaque clause d'initialisation, si l'une des conditions suivantes est satisfaite, elle se rapporte à l'élément d'agrégat correspondant elem :
-
- elem n'est pas un agrégat.
- La clause d'initialisation commence par { .
- La clause d'initialisation est une expression, et une séquence de conversion implicite peut être formée qui convertit l'expression vers le type de elem .
- elem est un agrégat qui n'a lui-même aucun élément agrégat.
- Sinon, elem est un agrégat et ce sous-agrégat est remplacé dans la liste des éléments d'agrégat par la séquence de ses propres éléments d'agrégat, et l'analyse d'appartenance reprend avec le premier de ces éléments et la même clause d'initialisation. En d'autres termes, ces règles s'appliquent récursivement aux sous-agrégats de l'agrégat.
L'analyse est terminée lorsque toutes les clauses d'initialisation ont été épuisées. Si une clause d'initialisation subsiste qui ne se rapporte pas à un élément de l'agrégat ou à l'un de ses sous-agrégats, le programme est mal formé.
struct S1 { long a, b; }; struct S2 { S1 s, t; }; // Chaque sous-agrégat de « x » est rattaché à une clause d'initialisation commençant par { S2 x[2] = { // rattaché à « x[0] » { {1L, 2L}, // rattaché à « x[0].s » {3L, 4L} // rattaché à « x[0].t » }, // rattaché à « x[1] » { {5L, 6L}, // rattaché à « x[1].s » {7L, 8L} // rattaché à « x[1].t » } }; // « x » et « y » ont la même valeur (voir ci-dessous) S2 y[2] = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L}; // Processus de l'analyse de rattachement de « y » : // 1. Initialise la séquence des éléments d'agrégat (x[0], x[1]) et // la séquence des clauses d'initialisation (1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L). // 2. En partant des premiers éléments de chaque séquence, // vérifie si 1L est rattaché à x[0] : // · x[0] est un agrégat. // · 1L ne commence pas par {. // · 1L est une expression, mais ne peut pas être converti implicitement en S2. // · x[0] a des éléments d'agrégat. // 3. 1L ne peut pas être rattaché à x[0], donc x[0] est remplacé par x[0].s et x[0].t, // la séquence des éléments d'agrégat devient (x[0].s, x[0].t, x[1]). // 4. Reprend la vérification de rattachement, mais 1L ne peut pas non plus être rattaché à x[0].s. // 5. La séquence des éléments d'agrégat devient maintenant (x[0].s.a, x[0].s.b, x[0].t, x[1]). // 6. Reprend la vérification de rattachement à nouveau : // 1L est rattaché à x[0].s.a, et 2L est rattaché à x[0].s.b. // 7. Le reste de l'analyse de rattachement fonctionne de manière similaire. char cv[4] = {'a', 's', 'd', 'f', 0}; // Erreur : trop de clauses d'initialisation
Processus d'initialisation
Détermination du type d'élément
Les effets de l'initialisation par agrégat sont :
|
(depuis C++20) |
-
- Sinon, (depuis C++20) si la liste d'initialisation n'est pas vide, les éléments explicitement initialisés de l'agrégat sont les éléments avec une clause d'initialisation rattachée et les éléments ayant un sous-agrégat avec une clause d'initialisation rattachée.
- Sinon, la liste d'initialisation doit être vide ( { } ), et il n'y a pas d'éléments explicitement initialisés.
- Le programme est mal formé si l'agrégat est une union et qu'il y a deux ou plusieurs éléments explicitement initialisés :
union u { int a; const char* b; }; u a = {1}; // OK : initialise explicitement le membre `a` u b = {0, "asdf"}; // erreur : initialise explicitement deux membres u c = {"asdf"}; // erreur : int ne peut pas être initialisé par "asdf" // Listes d'initialisation désignées C++20 u d = {.b = "asdf"}; // OK : peut initialiser explicitement un membre non-initial u e = {.a = 1, .b = "asdf"}; // erreur : initialise explicitement deux membres
Éléments explicitement initialisés
Pour chaque élément explicitement initialisé :
struct C { union { int a; const char* p; }; int x; } c = {.a = 1, .x = 3}; // initializes c.a with 1 and c.x with 3
|
(depuis C++20) |
|
(until C++20) |
|
(since C++20) |
-
- Si une clause d'initialisation se rapporte à l'élément d'agrégat, alors l'élément d'agrégat est copy-initialized à partir de la clause d'initialisation.
- Sinon, l'élément d'agrégat est copy-initialized à partir d'une liste d'initialisation entre accolades comprenant toutes les clauses d'initialisation qui se rapportent aux sous-objets de l'élément d'agrégat, dans l'ordre d'apparition.
struct A { int x; struct B { int i; int j; } b; } a = {1, {2, 3}}; // initialise a.x avec 1, a.b.i avec 2, a.b.j avec 3 struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // initialise d1.b1 avec 1, d1.b2 avec 2, // d1.b3 avec 42, d1.d avec 4 derived d2{{}, {}, 4}; // initialise d2.b1 avec 0, d2.b2 avec 42, // d2.b3 avec 42, d2.d avec 4
Éléments initialisés implicitement
Pour un agrégat non-union, chaque élément qui n'est pas un élément explicitement initialisé est initialisé comme suit :
|
(depuis C++11) |
- Sinon, si l'élément n'est pas une référence, l'élément est copy-initialized à partir d'une liste d'initialisation vide.
- Sinon, le programme est mal formé.
struct S { int a; const char* b; int c; int d = b[a]; }; // initialise ss.a avec 1, // ss.b avec "asdf", // ss.c avec la valeur d'une expression de la forme int{} (c'est-à-dire 0), // et ss.d avec la valeur de ss.b[ss.a] (c'est-à-dire 's') S ss = {1, "asdf"};
Si l'agrégat est une union et que la liste d'initialisation est vide, alors
|
(depuis C++11) |
- Sinon, le premier membre de l'union (s'il existe) est initialisé par copie à partir d'une liste d'initialisation vide.
Tableaux avec des limites inconnues
Le nombre d'éléments dans un tableau de taille inconnue initialisé avec une liste d'initialisation entre accolades est le nombre d'éléments explicitement initialisés du tableau. Un tableau de taille inconnue ne peut pas être initialisé avec { } .
int x[] = {1, 3, 5}; // x possède 3 éléments struct Y { int i, j, k; }; Y y[] = {1, 2, 3, 4, 5, 6}; // y ne possède que 2 éléments : // 1, 2 et 3 appartiennent à y[0], // 4, 5 et 6 appartiennent à y[1] int z[] = {} // Erreur : impossible de déclarer un tableau sans aucun élément
Initialisateurs désignésLes formes syntaxiques (3,4) sont appelées initialisateurs désignés : chaque désignateur doit nommer un membre de données direct non statique de T, et tous les désignateurs utilisés dans l'expression doivent apparaître dans le même ordre que les membres de données de T. struct A { int x; int y; int z; }; A a{.x = 1, .y = 2, .z = 3}; // ok A b{.y = 2, .z = 3, .x = 1}; // error; designator order does not match declaration order Chaque membre de données direct non statique nommé par l'initialisateur désigné est initialisé à partir de l'initialisateur entre accolades ou égal correspondant qui suit le désignateur. Les conversions restrictives sont interdites. Les initialisateurs désignés peuvent être utilisés pour initialiser une union dans un état autre que le premier. Un seul initialisateur peut être fourni pour une union. union u { int a; const char* b; }; u f = {.b = "asdf"}; // OK, active member of the union is b u g = {.a = 1, .b = "asdf"}; // Error, only one initializer may be provided Pour un agrégat non-union, les éléments pour lesquels aucun initialisateur désigné n'est fourni sont initialisés de la même manière que décrit ci-dessus lorsque le nombre de clauses d'initialisation est inférieur au nombre de membres (initialisateurs de membres par défaut lorsqu'ils sont fournis, initialisation de liste vide sinon) : struct A { string str; int n = 42; int m = -1; }; A{.m = 21} // Initializes str with {}, which calls the default constructor // then initializes n with = 42 // then initializes m with = 21 struct A { int x; int y; int z; }; A a{.x = 1, .z = 2}; // ok, b.y initialized to 0 A b{.y = 2, .x = 1}; // error; designator order does not match declaration order A c{.y = 2}; // ok, c.x and c.z are initialized to 0 constexpr A d{.z = 2}; // can be used with constexpr, as opposed to: constexpr A d; static_assert(d.x == 0 && d.y == 0); // d.x and d.y are initialized to 0 Si l'agrégat initialisé avec une clause d'initialisateur désigné a un membre union anonyme, le désignateur correspondant doit nommer l'un des membres de cette union anonyme. Remarque : l'initialisation désignée dans le désordre, l'initialisation désignée imbriquée, le mélange d'initialisateurs désignés et d'initialisateurs réguliers, et l'initialisation désignée de tableaux sont tous pris en charge dans le langage de programmation C , mais ne sont pas autorisés en C++. struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ (out of order) int arr[3] = {[1] = 5}; // valid C, invalid C++ (array) struct B b = {.a.x = 0}; // valid C, invalid C++ (nested) struct A a = {.x = 1, 2}; // valid C, invalid C++ (mixed) |
(depuis C++20) |
Tableaux de caractères
Les tableaux de types de caractères ordinaires ( char , signed char , unsigned char ) , char8_t (depuis C++20) , char16_t , char32_t (depuis C++11) , ou wchar_t peuvent être initialisés à partir de littéraux de chaîne ordinaires , de littéraux de chaîne UTF-8 (depuis C++20) , de littéraux de chaîne UTF-16, de littéraux de chaîne UTF-32 (depuis C++11) , ou de littéraux de chaîne larges, respectivement, optionnellement entourés d'accolades . De plus, un tableau de char ou unsigned char peut être initialisé par un littéral de chaîne UTF-8, optionnellement entouré d'accolades (depuis C++20) . Les caractères successifs du littéral de chaîne (incluant le caractère nul de fin implicite) initialisent les éléments du tableau , avec une conversion intégrale si nécessaire pour les valeurs source et destination (depuis C++20) . Si la taille du tableau est spécifiée et qu'elle est supérieure au nombre de caractères dans le littéral de chaîne, les caractères restants sont initialisés à zéro.
char a[] = "abc"; // équivalent à char a[4] = {'a', 'b', 'c', '\0'}; // unsigned char b[3] = "abc"; // Erreur : chaîne d'initialisation trop longue unsigned char b[5]{"abc"}; // équivalent à unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'}; wchar_t c[] = {L"кошка"}; // accolades optionnelles // équivalent à wchar_t c[6] = {L'к', L'о', L'ш', L'к', L'а', L'\0'};
Notes
Une classe agrégée ou un tableau peut inclure des bases publiques non agrégées (depuis C++17) , des membres ou des éléments, qui sont initialisés comme décrit ci-dessus (par exemple, l'initialisation par copie à partir de la clause d'initialisation correspondante).
Jusqu'à C++11, les conversions restrictives étaient autorisées dans l'initialisation d'agrégats, mais elles ne sont plus permises désormais.
Jusqu'à C++11, l'initialisation d'agrégat ne pouvait être utilisée que dans la définition de variables, et ne pouvait pas être utilisée dans une liste d'initialisation de constructeur , une expression new , ou la création d'objets temporaires en raison de restrictions syntaxiques.
En C, un tableau de caractères d'une taille inférieure d'un à la taille du littéral de chaîne peut être initialisé à partir d'un littéral de chaîne ; le tableau résultant n'est pas terminé par un caractère nul. Ceci n'est pas autorisé en C++.
| Macro de test de fonctionnalité | Valeur | Std | Fonctionnalité |
|---|---|---|---|
__cpp_aggregate_bases
|
201603L
|
(C++17) | Classes agrégées avec classes de base |
__cpp_aggregate_nsdmi
|
201304L
|
(C++14) | Classes agrégées avec initialiseurs de membres par défaut |
__cpp_aggregate_paren_init
|
201902L
|
(C++20) | Initialisation d'agrégat sous la forme d'une initialisation directe |
__cpp_char8_t
|
202207L
|
(C++23)
(DR20) |
char8_t correctif de compatibilité et de portabilité ( permettre l'initialisation de tableaux ( unsigned char à partir de littéraux de chaîne UTF-8 ) |
__cpp_designated_initializers
|
201707L
|
(C++20) | Initialisateurs désignés |
Exemple
#include <array> #include <cstdio> #include <string> struct S { int x; struct Foo { int i; int j; int a[3]; } b; }; int main() { S s1 = {1, {2, 3, {4, 5, 6}}}; S s2 = {1, 2, 3, 4, 5, 6}; // identique, mais avec élision d'accolades S s3{1, {2, 3, {4, 5, 6}}}; // identique, utilisant la syntaxe d'initialisation directe par liste S s4{1, 2, 3, 4, 5, 6}; // erreur jusqu'à CWG 1270 : // l'élision d'accolades n'est autorisée qu'avec le signe égal int ar[] = {1, 2, 3}; // ar est int[3] // char cr[3] = {'a', 'b', 'c', 'd'}; // trop de clauses d'initialisation char cr[3] = {'a'}; // tableau initialisé comme {'a', '\0', '\0'} int ar2d1[2][2] = {{1, 2}, {3, 4}}; // tableau 2D entièrement entre accolades : {1, 2} // {3, 4} int ar2d2[2][2] = {1, 2, 3, 4}; // élision d'accolades : {1, 2} // {3, 4} int ar2d3[2][2] = {{1}, {2}}; // seulement première colonne : {1, 0} // {2, 0} std::array<int, 3> std_ar2{{1, 2, 3}}; // std::array est un agrégat std::array<int, 3> std_ar1 = {1, 2, 3}; // élision d'accolades acceptée // int ai[] = {1, 2.0}; // conversion rétrécissante de double vers int : // erreur en C++11, acceptée en C++03 std::string ars[] = {std::string("one"), // initialisation par copie "two", // conversion, puis initialisation par copie {'t', 'h', 'r', 'e', 'e'}}; // initialisation par liste union U { int a; const char* b; }; U u1 = {1}; // OK, premier membre de l'union // U u2 = {0, "asdf"}; // erreur : trop d'initialisateurs pour l'union // U u3 = {"asdf"}; // erreur : conversion invalide vers int [](...) { std::puts("Garbage collecting unused variables... Done."); } ( s1, s2, s3, s4, ar, cr, ar2d1, ar2d2, ar2d3, std_ar2, std_ar1, u1 ); } // agrégat struct base1 { int b1, b2 = 42; }; // non-agrégat struct base2 { base2() : b3(42) {} int b3; }; // agrégat en C++17 struct derived : base1, base2 { int d; }; derived d1{{1, 2}, {}, 4}; // d1.b1 = 1, d1.b2 = 2, d1.b3 = 42, d1.d = 4 derived d2{{}, {}, 4}; // d2.b1 = 0, d2.b2 = 42, d2.b3 = 42, d2.d = 4
Sortie :
Garbage collecting unused variables... Done.
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 | Applicable à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 413 | C++98 | les champs de bits anonymes étaient initialisés dans l'initialisation d'agrégat | ils sont ignorés |
| CWG 737 | C++98 |
lorsqu'un tableau de caractères est initialisé avec un littéral de chaîne
ayant moins de caractères que la taille du tableau, les éléments de caractère après le ' \0 ' final n'étaient pas initialisés |
ils sont
initialisés à zéro |
| CWG 1270 | C++11 | l'élision des accolades n'était autorisée que dans l'initialisation de liste par copie | autorisée ailleurs |
| CWG 1518 | C++11 |
une classe qui déclare un constructeur par défaut explicite ou
a des constructeurs hérités pouvait être un agrégat |
ce n'est pas un
agrégat |
| CWG 1622 | C++98 | une union ne pouvait pas être initialisée avec { } | autorisé |
|
CWG 2149
( P3106R1 ) |
C++98 |
il n'était pas clair si l'élision des accolades est
applicable pendant la déduction de taille de tableau |
applicable |
| CWG 2272 | C++98 |
un membre référence non statique qui n'est pas explicitement
initialisé était initialisé par copie depuis une liste d'initialisation vide |
le programme est
incorrect dans ce cas |
| CWG 2610 | C++17 | les types agrégats ne pouvaient pas avoir de classes de base indirectes privées ou protégées | autorisé |
| CWG 2619 | C++20 | le type d'initialisation depuis les initialiseurs désignés n'était pas clair |
cela dépend du
type de l'initialiseur |
| P2513R4 | C++20 |
un littéral de chaîne UTF-8 ne pouvait pas initialiser un tableau de
char
ou unsigned char , ce qui était incompatible avec C ou C++17 |
une telle initialisation
est valide |
Voir aussi
|
Documentation C
pour
Initialisation des structures et unions
|