78 votes

C++ : initialisateur de constructeur pour les tableaux

J'ai une crampe au cerveau... comment initialiser correctement un tableau d'objets en C++ ?

exemple de non-réseau :

struct Foo { Foo(int x) { /* ... */  } };

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};

l'exemple du réseau :

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) {}
};

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

0 votes

Vos classes ne sont pas constructibles car tout est private .

5 votes

(les mots-clés d'accès ont été laissés de côté pour des raisons de simplicité pédagogique)

9 votes

Ne serait-il pas plus facile d'utiliser struct à la place de class pour des raisons de simplicité pédagogique ? Je trouve le code qui se compile plus facile à apprendre ;-)

60voto

AProgrammer Points 31212

Il n'y a pas moyen. Vous avez besoin d'un constructeur par défaut pour les membres du tableau et il sera appelé, ensuite, vous pouvez faire toute initialisation que vous voulez dans le constructeur.

8 votes

Malheureusement, vous avez raison. +1 Notez que la syntaxe d'initialisation unifiée de C++1x vous permettra de le faire.

0 votes

@sbi A moins que le constructeur requis ne soit marqué comme explicite

17voto

Michael Kristofik Points 16035

Actuellement, vous ne pouvez pas utiliser la liste d'initialisation pour les membres d'un tableau. Vous êtes obligé de le faire à la manière forte.

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}) {}
};

0 votes

Un constructeur à un argument sera appelé pour un int, sauf si vous déclarez le constructeur explicitement.

0 votes

Intéressant... J'aurais probablement dû utiliser autre chose que int puis dans mon exemple, car c'est trop "facile" à gérer :-)

7voto

UncleBens Points 24580

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

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

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

Vous pourriez utiliser un objet de type tableau qui es copiable (boost ou std::tr1) et initialiser à partir d'un tableau statique :

#include <boost/array.hpp>

struct Baz {

    boost::array<Foo, 3> foo;
    static boost::array<Foo, 3> initFoo;
    Baz() : foo(initFoo)
    {

    }
};

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };

0 votes

+1. Je me demandais pourquoi personne ne l'avait proposé, jusqu'à ce que je voie votre réponse. array est trivial à mettre en œuvre, et il n'est ni sauvage ni fou. Vous pourriez écrire une fonction comme array<Foo, 3> create() { array<Foo, 3> a = { ... }; return a; } pour éviter la variable statique, également.

0 votes

Cela me semble également évident, même si la prise en charge des modèles est faible sur le compilateur cible (pas de std::vector semble louche) une approche de génération fonctionnerait (préprocesseur ou script générant les classes nécessaires).

3voto

Nordlöw Points 3467

Vous pouvez utiliser C++0x auto ainsi que le mot-clé spécialisation des modèles sur par exemple une fonction nommée boost::make_array() (similaire à make_pair() ). Pour le cas où N est soit 1 soit 2 arguments, nous pouvons alors écrire variante A comme

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

et variante B comme

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

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

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

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

boost::array<int, 2> x = {{1,2}};

Pour types définis par l'utilisateur (UDT), cependant, la variante B donne lieu à un constructeur de copie supplémentaire qui ralentissent généralement les choses et sont donc à éviter.

Notez que boost::make_array des erreurs lorsqu'il est appelé avec des littéraux de tableaux 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 const char* les littéraux peuvent être trompeuse dans leur utilisation.

Modèles variadiques disponible dans GCC depuis la version 4.5, peut être utilisé pour réduire le code passe-partout de la spécialisation des modèles pour chaque élément du modèle. N en un définition du modèle unique de boost::make_array() défini comme suit

/*! Construct Array from @p a, @p b. */
template <typename T, typename ... R>
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
{
    return boost::array<T,1+sizeof...(R)>({{ a, b... }});
}

Cela fonctionne à peu près comme prévu. Le premier argument détermine boost::array argument de modèle T et tous les autres arguments sont convertis en T . Dans certains cas, cela peut être indésirable, mais je ne suis pas sûr de la manière dont il est possible de le spécifier en utilisant des modèles variadiques.

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

0 votes

Merci mais C++0x n'est pas disponible sur les processeurs embarqués bas de gamme (les compilateurs C++ sont assez difficiles à trouver).

1voto

Robert Points 5137

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X