2
votes

Liste d'initialiseurs entre accolades par défaut C ++

Disons que j'ai la classe c_student avec plusieurs membres et aucun constructeur:

c_student( std::initializer_list <???>) = default; << Pseudo-code only!

Ce code fonctionne bien, mais disons que je veux définir des constructeurs personnalisés à l'intérieur de la classe:

47_object_initialization.cpp:32:34: error: could not convert ‘{"Luke ", 420}’ from ‘<brace-enclosed initializer list>’ to ‘c_student’
   32 |  c_student stud_c  = {"Luke ",420};
      |                                  ^
      |                                  |
      |                                  <brace-enclosed initializer list>

Si je les ajoute à la classe, le compilateur se plaint que:

        c_student (std::string n):name(n){};
        c_student() = default;

Je veux continuer à utiliser le constructeur par défaut pour {} et j'ai donc besoin d'écrire quelque chose comme:

#include <iostream>
#include <string>
#include <initializer_list>
#include <typeinfo>

class c_student{
    // private:
    public:
        std::string name = "John";
        int         mark = 5;
        std::string second_name="Doe";

        void print(){
            std::cout << name <<" " << second_name << " : "<< mark<<"\n";
        }
    };

int main(){
    c_student stud_c  = {"Luke ",420};
    stud_c.print();
    c_student stud_d  = {"Mark "};
    stud_d.print();
        
}

Mais je ne peux pas comprendre exactement comment. Quelqu'un pourrait-il attirer mon attention sur la bonne page de référence du RPC?


0 commentaires

3 Réponses :


2
votes

Ce n'est malheureusement pas possible. La raison pour laquelle votre code a fonctionné à l'origine est due à l' initialisation de l'agrégat . Dès que vous avez créé un constructeur défini par l'utilisateur, votre classe n'est plus un type d'agrégat

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

  • type de classe qui a
    - aucun constructeur déclaré par l'utilisateur

3 commentaires

Êtes-vous sûr? Peut-être que ce n'est possible que dans les normes ultérieures, mais il y a quelques informations connexes ici: stackoverflow.com/questions/21941967/...


@SvenNilsson La raison pour laquelle le code fonctionne est qu'il a fourni un constructeur défini par l'utilisateur pour B , ce que l'OP est libre de faire ici aussi. Cependant, l'OP souhaite ici "ramener" le comportement d'initialisation default agrégats, ce qui, comme le mentionne la norme, n'est pas possible. Ils devraient définir un constructeur défini par l'utilisateur pour émuler ce type de comportement, ce qui mine le comportement généré default compilateur.


OK, je comprends. Vous avez raison, mais il existe une solution de contournement ...



2
votes

Vous pouvez utiliser l'initialisation d'accolades pour obtenir ce que vous voulez en remplaçant tous les constructeurs par le suivant:

c_student (std::string name = "John", int mark = 5, std::string second_name = "Doe") 
  : name(name), mark(mark), second_name(second_name) {};

De plus, vous pouvez supprimer les initialiseurs de membres par défaut car la seule façon de construire un c_student leur donnera les valeurs par défaut si elles ne sont pas fournies.

Voici une démo .


4 commentaires

Sympa et efficace. Cependant, que se passe-t-il si l'ordre des éléments de la liste est différent, comme {"First", "Last", 20} ?


@AdrianMole Sans les initialiseurs désignés par C ++ 20, il n'y a aucun moyen de se moquer de l'ordre des paramètres.


Vous pouvez fournir des constructeurs explicites (et séparés) avec différents ordres d'arguments.


@AdrianMole Parfois, mais je pense que tous les arguments devraient être de types différents. Par exemple, comment c_student("Adrian") vous l' c_student("Adrian") entre c_student("Adrian") ? Est-ce le premier ou le deuxième nom? Hmm, en fait, UDL fonctionnerait comme un correctif pour cela, mais c'est beaucoup de travail juste pour différents ordres de constructeur.



1
votes

Vous pouvez ajouter un autre constructeur défini par l'utilisateur pour gérer la liste d'initialisation avec deux entrées (et continuer à ajouter de tels constructeurs pour chaque taille et ordre de liste possibles). Les éléments suivants se compilent et fonctionnent comme prévu:

#include <iostream>
#include <string>

class c_student {
private:
    std::string name = "John";
    int         mark = 5;
    std::string second_name = "Doe";
public:
    void print() {
        std::cout << name << " " << second_name << " : " << mark << "\n";
    }
    c_student(std::string n) : name(n) { }  // Called for initializer list {"xxx"}
    c_student(std::string n, int m) : name(n), mark(m) { } // Called for list {"xxx", m}
    c_student(std::string n, std::string s, int m) : name(n), mark(m), second_name(s) { } // Called for list {"xxx", "yyy", m}
    c_student() = default;
};

int main()
{
    c_student stud_c = { "Luke", 420 };
    stud_c.print();
    c_student stud_d = { "Mark" };
    stud_d.print();
    c_student stud_e = { "John", "Smith", 333 };
    stud_e.print();
    return 0;
}


0 commentaires