Namespaces
Variants

Aggregate 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

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)
1,2) Initialisation d'un agrégat avec une liste d'initialisation ordinaire.
3,4) Initialisation d'un agrégat avec initialiseurs désignés (classe agrégat uniquement).

Définitions

Agrégat

Un agrégat est l'un des types suivants :

  • types de tableau
  • types de classe qui ont
  • aucun constructeur déclaré par l'utilisateur
(jusqu'à C++11)
(depuis C++11)
(jusqu'à C++20)
  • aucun constructeur déclaré par l'utilisateur ou hérité
(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
  • pour une classe, les membres de données non statiques qui ne sont pas des champs de bits anonymes, dans l'ordre de déclaration.
(jusqu'en C++17)
  • pour une classe, les classes de base directes dans l'ordre de déclaration, suivies des membres de données non statiques directs qui ne sont ni des champs de bits anonymes ni membres d'une union anonyme , dans l'ordre de déclaration.
(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 :

1) Déterminez les éléments explicitement initialisés de l'agrégat comme suit :
  • Si la liste d'initialisation est une liste d'initialisation désignée (l'agrégat ne peut être que de type classe), l'identifiant dans chaque désignateur doit nommer un membre de données direct non statique de la classe, et les éléments explicitement initialisés de l'agrégat sont les éléments qui sont, ou contiennent, ces membres.
(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
2) Initialise chaque élément de l'agrégat dans l'ordre des éléments. Autrement dit, tous les calculs de valeurs et effets secondaires associés à un élément donné sont séquencés avant ceux de tout élément qui le suit dans l'ordre (depuis C++11) .

Éléments explicitement initialisés

Pour chaque élément explicitement initialisé :

  • Si l'élément est un membre d'union anonyme et que la liste d'initialisation est une liste d'initialisation désignée , l'élément est initialisé par la liste d'initialisation désignée { D } , où D est la clause d'initialisation désignée nommant un membre de l'union anonyme. Il ne doit y avoir qu'une seule telle clause d'initialisation désignée.
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
  • Sinon, si la liste d'initialisation est une liste d'initialisation désignée, l'élément est initialisé avec l'initialiseur de la clause d'initialisation désignée correspondante.
  • Si cet initialiseur est de syntaxe (1) , et qu'une conversion rétrécissante est requise pour convertir l'expression, le programme est mal formé.
(depuis C++20)


  • La liste d'initialisation est une liste d'initialisation entre accolades :
(until C++20)
  • Sinon, la liste d'initialisation est une liste d'initialisation non désignée entre accolades :
(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

  • Si un membre variant possède un initialiseur de membre par défaut, ce membre est initialisé à partir de son initialiseur de membre par défaut.
(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és

Les 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