C ++: initialiseur de constructeur pour les tableaux

J’ai une crampe cérébrale … comment initialiser un tableau d’objects correctement en C ++?

exemple non-tableau:

struct Foo { Foo(int x) { /* ... */ } }; struct Bar { Foo foo; Bar() : foo(4) {} }; 

exemple de tableau:

 struct Foo { Foo(int x) { /* ... */ } }; struct Baz { Foo foo[3]; // ??? I know the following syntax is wrong, but what's correct? Baz() : foo[0](4), foo[1](5), foo[2](6) {} }; 

edit: Les idées de contournement folles et folles sont appréciées, mais elles ne m’aideront pas dans mon cas. Je travaille sur un processeur incorporé où std :: vector et d’autres constructions STL ne sont pas disponibles, et la solution évidente consiste à créer un constructeur par défaut et à avoir une méthode init() qui peut être appelée après la construction, de sorte que Je n’ai pas du tout besoin d’utiliser des initialiseurs. (C’est l’un des cas où j’ai été gâté par le mot-clé final de Java + la flexibilité avec les constructeurs.)

    Il n’y a pas moyen. Vous avez besoin d’un constructeur par défaut pour les membres du tableau et il sera appelé, après, vous pouvez effectuer toute initialisation souhaitée dans le constructeur.

    Juste pour mettre à jour cette question pour C ++ 11, ceci est maintenant possible à la fois et très naturel:

     struct Foo { Foo(int x) { /* ... */ } }; struct Baz { Foo foo[3]; Baz() : foo{{4}, {5}, {6}} { } }; 

    Ces accolades peuvent aussi être éludées pour un rendu encore plus concis:

     struct Baz { Foo foo[3]; Baz() : foo{4, 5, 6} { } }; 

    Qui peut facilement être étendu aux tableaux multidimensionnels aussi:

     struct Baz { Foo foo[3][2]; Baz() : foo{1, 2, 3, 4, 5, 6} { } }; 

    En ce moment, vous ne pouvez pas utiliser la liste d’initialisation pour les membres du tableau. Vous êtes coincé à le faire à la dure.

     class Baz { Foo foo[3]; Baz() { foo[0] = Foo(4); foo[1] = Foo(5); foo[2] = Foo(6); } }; 

    En C ++ 0x, vous pouvez écrire:

     class Baz { Foo foo[3]; Baz() : foo({4, 5, 6}) {} }; 

    Malheureusement, il n’y a aucun moyen d’initialiser les membres du tableau jusqu’à C ++ 0x.

    Vous pouvez utiliser un std :: vector et push_back les instances de Foo dans le corps du constructeur.

    Vous pourriez donner à Foo un constructeur par défaut (peut-être privé et faire de Baz un ami).

    Vous pouvez utiliser un object tableau copiable (boost ou std :: tr1) et initialiser à partir d’un tableau statique:

     #include  struct Baz { boost::array foo; static boost::array initFoo; Baz() : foo(initFoo) { } }; boost::array Baz::initFoo = { 4, 5, 6 }; 

    Vous pouvez utiliser auto mot auto clé auto C ++ 0x avec une spécialisation de modèle , par exemple sur une fonction nommée boost::make_array() (similaire à make_pair() ). Pour le cas où N est 1 ou 2 arguments, on peut alors écrire la variante A comme

     namespace boost { /*! Construct Array from @p a. */ template  boost::array make_array(const T & a) { return boost::array ({{ a }}); } /*! Construct Array from @pa, @p b. */ template  boost::array make_array(const T & a, const T & b) { return boost::array ({{ a, b }}); } } 

    et la variante B comme

     namespace boost { /*! Construct Array from @p a. */ template  boost::array make_array(const T & a) { boost::array x; x[0] = a; return x; } /*! Construct Array from @pa, @p b. */ template  boost::array make_array(const T & a, const T & b) { boost::array x; x[0] = a; x[1] = b; return x; } } 

    GCC-4.6 avec -std=gnu++0x et -O3 génère exactement le même code binary pour

     auto x = boost::make_array(1,2); 

    en utilisant à la fois A et B comme il le fait pour

     boost::array x = {{1,2}}; 

    Pour les types définis par l’ utilisateur (UDT), la variante B génère un constructeur de copie supplémentaire , ce qui ralentit généralement les choses et doit donc être évité.

    Notez que les erreurs boost::make_array lors de l’appel avec des littéraux de tableau de caractères explicites comme dans le cas suivant

     auto x = boost::make_array("a","b"); 

    Je pense que c’est une bonne chose car les caractères littéraux peuvent être trompeurs dans leur utilisation.

    Les modèles variadiques , disponibles dans GCC depuis la version 4.5, peuvent être utilisés pour réduire tous les codes de chaudière de spécialisation de modèle pour chaque N dans une définition de modèle unique de boost::make_array() définie comme

     /*! Construct Array from @pa, @p b. */ template  boost::array make_array(T a, const R & ... b) { return boost::array({{ a, b... }}); } 

    Cela fonctionne à peu près comme prévu. Le premier argument détermine l’argument T boost::array et tous les autres arguments sont convertis en T Dans certains cas, cela peut être indésirable, mais je ne sais pas comment, si cela est possible, utiliser des modèles variadiques.

    Peut-être que boost::make_array() devrait aller dans les bibliothèques Boost?

    Cela semble fonctionner, mais je ne suis pas convaincu que ce soit juste:

     #include  struct Foo { int x; Foo(int x): x(x) { } }; struct Baz { Foo foo[3]; static int bar[3]; // Hmm... Baz() : foo(bar) {} }; int Baz::bar[3] = {4, 5, 6}; int main() { Baz z; std::cout << z.foo[1].x << "\n"; } 

    Sortie:

     $ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit g++ -pedantic arrayinit.cpp -o arrayinit 5 

    Caveat emptor.

    Edit: non, Comeau le rejette.

    Une autre édition: C'est une sorte de sortingche, cela pousse simplement l'initialisation du tableau membre par membre à un endroit différent. Donc, il faut toujours que Foo ait un constructeur par défaut, mais si vous n'avez pas std::vector vous pouvez implémenter vous-même le ssortingct minimum absolu dont vous avez besoin:

     #include  struct Foo { int x; Foo(int x): x(x) { }; Foo(){} }; // very ssortingpped-down replacement for vector struct Three { Foo data[3]; Three(int d0, int d1, int d2) { data[0] = d0; data[1] = d1; data[2] = d2; } Foo &operator[](int idx) { return data[idx]; } const Foo &operator[](int idx) const { return data[idx]; } }; struct Baz { Three foo; static Three bar; // construct foo using the copy ctor of Three with bar as parameter. Baz() : foo(bar) {} // or get rid of "bar" entirely and do this Baz(bool) : foo(4,5,6) {} }; Three Baz::bar(4,5,6); int main() { Baz z; std::cout << z.foo[1].x << "\n"; } 

    z.foo n'est pas réellement un tableau, mais il ressemble autant à un vecteur qu'à un autre. Ajouter des fonctions begin() et end() à Three est sortingvial.

    Seul le constructeur par défaut peut être appelé lors de la création d’objects dans un tableau.

    Dans le cas spécifique où le tableau est un membre de données de la classe, vous ne pouvez pas l’ initialiser dans la version actuelle du langage. Il n’y a pas de syntaxe pour cela. Soit fournir un constructeur par défaut pour les éléments de tableau ou utiliser std::vector .

    Un tableau autonome peut être initialisé avec l’initialiseur d’agrégat

     Foo foo[3] = { 4, 5, 6 }; 

    mais malheureusement, il n’y a pas de syntaxe correspondante pour la liste d’initialisation du constructeur.

    Il n’y a pas de syntaxe de construction de tableau qui puisse être utilisée dans ce contexte, du moins pas directement. Vous pouvez accomplir ce que vous essayez d’accomplir par quelque chose du genre:

     Bar::Bar() { static const int inits [] = {4,5,6}; static const size_t numInits = sizeof(inits)/sizeof(inits[0]); std::copy(&inits[0],&inits[numInits],foo); // be careful that there are enough slots in foo } 

    … mais vous devrez donner à Foo un constructeur par défaut.

    Des idées d’un esprit tordu:

     class mytwistedclass{ static std::vector initVector; mytwistedclass() { //initialise with initVector[0] and then delete it :-) } }; 

    placez maintenant ce initVector à quelque chose que vous voulez avant d’instancier un object. Ensuite, vos objects sont initialisés avec vos parameters.

    Vous pouvez le faire, mais ce n’est pas joli:

     #include  class A { int mvalue; public: A(int value) : mvalue(value) {} int value() { return mvalue; } }; class B { // TODO: hack that respects alignment of A.. maybe C++14's alignof? char _hack[sizeof(A[3])]; A* marr; public: B() : marr(reinterpret_cast(_hack)) { new (&marr[0]) A(5); new (&marr[1]) A(6); new (&marr[2]) A(7); } A* arr() { return marr; } }; int main(int argc, char** argv) { B b; A* arr = b.arr(); std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n"; return 0; } 

    Si vous mettez cela dans votre code, j'espère que vous avez une très bonne raison.

    Ceci est ma solution pour votre référence:

     struct Foo { Foo(){}//used to make comstackr happy! Foo(int x){/*...*/} }; struct Bar { Foo foo[3]; Bar() { //initialize foo array here: for(int i=0;i<3;++i) { foo[i]=Foo(4+i); } } }; 

    dans le studio visuel 2012 ou supérieur, vous pouvez faire comme ça

     struct Foo { Foo(int x) { /* ... */ } }; struct Baz { Foo foo[3]; Baz() : foo() { } }; 
     class C { static const int myARRAY[10]; // only declaration !!! public: C(){} } const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9}; // here is definition int main(void) { C myObj; }