7
votes

Comment sur place initialiser un tableau?

Comment puis-je initialiser un tableau sans copier ou développer des éléments temporaires? Lorsque l'élément a explicitement Supprimer code> D copy ou déplacer constructeur, je ne peux initialiser que si l'élément a une CTOR par défaut ou une CTOR avec tous les arguments par défaut et je fais l'une des opérations suivantes: ) Déclarez clairement le tableau, (b) Initialiser et zéro Initialiser le tableau, ou (C) Initialiser et zéro Initialiser le tableau. Aucune initialisation directe (mais pas zéro) ni compilation de copie (mais pas zéro) compile.

struct Foo
{
    Foo(int n = 5) : num(n) {}
    Foo(const Foo&) = delete;
    //Foo(Foo&&) = delete;  // <-- gives same effect
    int num;
};

int main()
{
    // Resultant arrays for 'a1', 'a2', and 'a3' are two
    // 'Foo' elements each with 'num' values of '5':

    Foo a1[2];          // plain declaration
    Foo a2[2] {};       // direct initialization and zero initialization
    Foo a3[2] = {};     // copy initialization and zero initialization
    Foo a4[2] {5, 5};   // direct initialization -> ERROR
    Foo a5[2] = {5, 5}; // copy initialization   -> ERROR
}


5 commentaires

Que diriez-vous de foo a [] = {foo (5), foo (5)}; ?


Notez que C ++ 17 a garanti une élision de copie garantie, de sorte que des cas mal formés en C ++ 14 deviendront bien formés.


@Kerreksb, foo A [] = {foo (5), foo (5)}; donne erreur: Utilisation de la fonction supprimée 'FOO :: FOO (const foo &)'


Il n'existe pas de "copie-zéro" et d'initialisation "directe-zéro". Ce sont des copies-list-initialisation et une initialisation directe-liste avec la liste vide. Et ils ne sont en aucune sensation zéro-initialisation, car foo sera initialisé par son constructeur.


La mise à jour Visual C ++ 2015 2 compile l'initialisation directe.


4 Réponses :


1
votes

Vous pouvez également créer un pointeur avec MALLOC, puis utiliser la syntaxe Array sur elle (si la classe est une pod). Ex: xxx

Il existe également un moyen d'initialiser un tableau comme une valeur temporaire, mais j'ai oublié comment faire ça.

Vous pouvez initialiser directement un tableau d'objets Si la classe est une pod (anciennes données simples). Pour qu'une classe soit une pod, il ne doit avoir aucun constructeur, destructeurs ou méthodes virtuelles. Tout dans la classe doit également être déclaré public pour qu'elle soit une cosse. Fondamentalement, une classe de pod est juste une structure de style C qui peut avoir des méthodes dedans.


6 commentaires

Sa question concerne une gamme d'objets de classe.


En C ++, il s'agit d'un comportement indéfini d'écrire dans l'espace Malloc'd sans utiliser de placement nouveau pour créer des objets dans l'espace. lecture supplémentaire


Veuillez consulter la réponse liée à mon commentaire - il n'est pas autorisé à écrire dans un stockage alloué à moins que des objets aient été créés dans le stockage. (C ++ diffère de C à cet égard)


Quand je mets cet exemple plus tôt, je voulais que ce soit pour les primitives seulement.


Vous n'avez évidemment toujours pas regardé le code dans la réponse que j'ai liée.


La classe que j'ai créée ci-dessus est un type de pod, ce qui signifie que c'est une primitive.



4
votes

La déclaration de code foo A2 [2]; déclare un tableau. Le seul moyen d'initialiser un tableau consiste via l'initialisation de la liste (c'est-à-dire une liste de zéro-ci-jointe de zéro ou plus), et le comportement est décrit par la section de la norme intitulée Initialisation globale . (Le terme l'agrégat fait référence aux tableaux et aux classes qui répondent à certains critères).

dans l'initialisation globale, la présence de = ne fait aucune différence. La définition de base de celui-ci est en C ++ 14 [dcl.init.aggr] / 2:

Lorsqu'un agrégat est initialisé par une liste d'initialistes, comme spécifié dans 8.5.4, les éléments de la liste d'initialistes sont considérés comme des initialisateurs pour les membres de l'agrégat, dans l'augmentation de l'indice ou de l'ordre des membres. Chaque membre est initialisé de copie à partir de la clause d'initialisateur correspondante.

aussi, / 7:

S'il y a moins d'initialistes-clauses dans la liste que des membres dans l'agrégat, chaque membre non explicitement initialisé doit être initialisé à partir de son initialisateur de bras ou égal ou, s'il n'y a pas de corset-ou-égal - Initializer, à partir d'une liste d'initialiseurs vide (8.5.4).

Vous pouvez voir à ce sujet que la copie-initialisation est toujours utilisée pour chaque initialisateur fourni. Par conséquent, lorsque l'initialiseur est une expression, un constructeur de copie / déplacement accessible doit exister pour la classe.

Toutefois (comme suggéré par ANTY), vous pouvez rendre l'initialisateur une autre liste. L'initialisation-initialisation à l'aide d'une liste est appelée copie-list-initialisation: xxx

quand un seul foo est initialisé, il n'est pas l'initialisation de l'agrégat (Depuis FOO n'est pas un agrégat). Les règles sont donc différentes de celles discutées ci-dessus. Copier-liste-initialisation d'une classe non agrégée vient sous la liste-initialisation, dans [dcl.init.list] /3.4, qui spécifie pour foo que les initialisateurs de la liste sont assortis aux arguments du constructeur. en utilisant la résolution de la surcharge. A ce stade, le constructeur foo (int) sera choisi, ce qui signifie que le constructeur de copie n'est pas requis.


Pour la complétude, je vais mentionner le option nucléaire : xxx

évidemment c'est un Dernier recours pour quand vous ne pouvez pas atteindre votre objectif par aucun autre moyen.


Note 1: Le ci-dessus s'applique à C ++ 14. En C ++ 17, je pense que la soi-disant "élection de copie garantie" modifiera l'initialisation de la copie en cas de neige pas réellement d'un constructeur de copie / déplacement. J'espère mettre à jour cette réponse une fois la norme publiée. Il y a également eu un triple avec une initialisation globale dans les brouillons.


6 commentaires

Par "INITIALISATION IN-PLACE", je veux construire des éléments directement dans la matrice lors de la construction de la matrice. Je veux ceci: foo a4 [2] {5, 5} , mais il est possible de construire temporaire foo S avant de les copier. Suppression de la copie CTOR Breaks Compilation. Je dis cela pour distinguer int m [2]; qui donne des éléments prématurés initiaux et nécessite la modification de la modification de la suite. Bien que foo ait un argument par défaut, je dis cela pour souligner que je souhaite construire la matrice avec n'importe quelle valeur client au moment de la construction, ne le laissez pas nécessairement utiliser l'argument par défaut.


@Codebricks int m [2]; n'a pas d'initialisateur (et serait décrit comme Initialisation par défaut ). L'affectation ultérieure n'est pas l'initialisation.


Il n'y a pas de différence dans mon esprit entre "Initialize Initialiser" et "Initialize" concernant la création d'une valeur initiale. Je veux dire "in-place" dans des termes similaires à std :: vecteur :: emplace_back qui transmet les arguments au constructeur de l'élément pour construire l'élément directement dans la matrice interne du vecteur; Sauf dans mon cas de la matrice de pile, je veux que cette construction ait lieu pendant la construction de la matrice, pas de placement nouveau ou d'attribution à celui-ci après sa construction. Je ne veux pas construire des éléments temporaires dans la liste bloquée pour copier-construire.


@ M. Je me rends compte que cela fait 4 ans depuis l'OP, mais j'espère que vous pourriez expliquer quelque chose en ce qui concerne votre réponse. La première citation que vous utilisez stipule que "(...) chaque membre est initialisée à partir de la clause d'initialisateur correspondante", qui implique que foo a [2] = {{3}, {4}} < / Code> Les éléments suivants doivent être Copier initialisés, qui, avant la C ++ 17, devrait provoquer une erreur de compilation. La CPPRAGENCE dit que cela devrait en fait fonctionner, mais cela est décrit différemment et, pour être franc, la citation que vous avez fournie me fait penser que cela ne devrait pas. C'est un peu déroutant.


@MDX Dans votre exemple, l'initialiszer est {3} et pas 3 . Donc, comme un exemple minimal plus simple, comparez foo b = 3; et foo b = {3}; . Avant C ++ 17 Néanmoins, le premier est une erreur due à la copie-initialisation équivalente à FOO B = FOO (3) nécessitant une copie / déplacement constructeur. Cependant, foo b = {3}; est une initialisation de copie à partir d'une liste, appelée copier-list-initialisation dont la sémantique est un peu différent de la copie non-liste. initialisation; Dans ce cas, les éléments de liste sont pris en tant que paramètres de constructeur pour B . La distinction peut être plus claire si la CTOR de FOO a pris 2 arguments.


BTW Un bon moyen de poster des questions de suivi consiste à créer une nouvelle question et à inclure un lien référençant le Q / A que vous avez eu un suivi à



4
votes

Dans votre cas, vous pouvez toujours utiliser ces constructions: xxx

ou xxx


0 commentaires

1
votes

Je n'ai pas la norme C ++ à portée de main et citant ce serait probablement le seul moyen de prouver mes mots. Donc, pour répondre à chacune de vos questions, je ne peux que dire:

  1. non ce n'est pas tout. Je ne peux pas vous fournir une liste exhaustive de possibles, mais j'ai certainement utilisé ce qui suit avant:

    xx xxx

    1. L'initalisation BRACE n'est pas déclarée et copie, c'est une construction de langues distincte. Il pourrait très bien de construire des éléments. La seule situation où je ne suis pas sûr si cela s'applique est {FOO (5), FOO (5)} Initialisation , car il demande explicitement la création de temporaires. La variante {5, 5} est identique, car pour initialiser une matrice, vous avez besoin d'une liste des objets intitulées des FOO . Puisque vous ne le créez pas, il utilisera le constructeur pour les temporaires pour obtenir {foo (5), foo (5)} . Le {{5}, {5}} Compile de variante car le compilateur sait qu'il peut construire foo objet hors de fourni {5} initialisateur et Il n'a donc pas besoin de temporaire - bien que je ne connaisse pas la formulation standard exacte qui le permet.

    2. Non, je ne pense pas que ces bugs sont des bugs.

    3. Je me souviens d'une ligne dans la norme C ++ qui dit essentiellement qu'un compilateur peut toujours remplacer l'initialisation de l'affectation par initialisation directe lors de la création d'une nouvelle variable.

      xx xxx

      1. Comme je l'ai déjà signalé ci-dessus: non, vous pouvez initialiser en place le tableau, vous avez juste besoin d'un élément approprié d'initialistes. {5} sera intégré comme "un initialisateur pour l'objet FOO", alors que la nature 5 sera sous-ci comme "une valeur pouvant être convertie en un objet FOO temporaire". Les listes d'initialistes doivent généralement contenir une liste d'initialistes pour les éléments ou les éléments du type exact des éléments. Si quelque chose de différent est donné, un temporaire sera créé.

0 commentaires