web-dev-qa-db-fra.com

Constructeur de liste d'initialisation entre accolades

J'ai un phénotype de classe avec le constructeur suivant:

Phenotype(uint8 init[NUM_ITEMS]);

Je peux créer un phénotype comme celui-ci:

uint8 data[] = {0,0,0,0,0};
Phenotype p(data);

Mais j'obtiens une erreur lorsque j'essaye d'en créer une comme ceci:

Phenotype p = {0,0,0,0,0};

Sortie:

$ make
g++ -Wall -g main.cpp -std=c++0x
main.cpp: In function ‘int main(int, char**)’:
main.cpp:109: error: no matching function for call to ‘Phenotype::Phenotype(<brace-enclosed initializer list>)’
main.cpp:37: note: candidates are: Phenotype::Phenotype(uint8*)

L'erreur semble indiquer qu'il existe un moyen de définir un constructeur qui prend une liste d'initialisation entre accolades. Est-ce que quelqu'un sait comment cela pourrait être fait?

41
bretttolbert

Cela ne peut être fait que pour les agrégats (tableaux et certaines classes. Contrairement à la croyance populaire, cela fonctionne aussi pour de nombreux non-pods). Écrire un constructeur qui les prend n'est pas possible.

Puisque vous l'avez marqué comme "C++ 0x", cela est cependant possible. Les mots magiques sont "constructeur de liste d'initialisation". Cela va comme

Phenotype(std::initializer_list<uint8> c) {
  assert(c.size() <= std::size(m_array));
  std::copy(c.begin(), c.end(), m_array);
}

// used like
Phenotype p1{1, 2, 3};
Phenotype p2({1, 3, 2}); // works too
Phenotype p3(1, 2, 3); // doesn't work

Cependant, une telle initialisation construira par défaut le tableau, puis utilisera l'opérateur d'affectation. Si vous visez la vitesse et la sécurité (vous obtenez des erreurs de temps de compilation pour trop d'initialiseurs!), Vous pouvez également utiliser un constructeur ordinaire avec un modèle variadic.

Cependant, cela peut être plus générique que nécessaire (souvent une liste initializer_list suffit, en particulier pour les entiers simples). Il bénéficie d'une transmission parfaite, de sorte qu'un argument rvalue peut être déplacé construit dans un élément de tableau

template<typename ...T>
Phenotype(T&&...t):m_array{ std::forward<T>(t)... } {

}

// used like
Phenotype p1{1, 2, 3}; 
Phenotype p2(1, 2, 3); // works too
Phenotype p3({1, 2, 3}); // doesn't work   

C'est un choix difficile!

Edit Correction, le dernier fonctionne aussi, car nous n'avons pas créé le constructeur explicit, donc il peut utiliser le constructeur de copie de Phenotype, construisant un temporaire Phenotype objet et copiez-le dans p3. Mais ce n'est pas ce que nous voudrions vraiment que les appels soient :)

67

En C++ 0x, il semble que vous puissiez créer un constructeur pour cela. Je n'en ai aucune expérience moi-même, mais on dirait qu'il s'appelle initializer list-constructor .

Un conteneur peut implémenter un constructeur de liste d'initialisation comme ceci:

template<class E> class vector {
public:
    vector (std::initializer_list<E> s) // initializer-list constructor
    {
        reserve(s.size());  // get the right amount of space
        uninitialized_copy(s.begin(), s.end(), elem);   // initialize elements (in elem[0:s.size()))
        sz = s.size();  // set vector size
    }

    // ... as before ...
};
6
Magnus Hoff

Vous devez utiliser le type de modèle std :: initializer_list. Exemple:

#include <iostream>
class X {
    public:
        X (std::initializer_list<int> list) {
        for (auto i = list.begin(); i != list.end(); i++) {
                std::cout << *i << std::endl;
            }
        }
};


int main () {
    X x = {1,2,3,4,5};
}
3
evnu