OVH Cloud OVH Cloud

Conteneur heterogene

12 réponses
Avatar
Frédéric Mayot
Bonjour,

Soit une classe template Contenu :

template <typename T>
class Contenu {T t;};

Je cherche à réaliser une classe Conteneur pour des objets Contenu avec
des paramètres de template différents.

A présent, j'ai écrit ceci :

class Base{};

template <typename T>
class Contenu : public Base {T t;};

class Conteneur
{
protected:
std::vector<Base> objets;
public:
template <typename T>
void ajoute(T& b) {objets.push_back(b);}
Base& get(int pos) {return objets[pos];}
};

int main(int argv, char** argc)
{
Conteneur mon_conteneur;
Contenu<int> mon_contenu;
mon_conteneur.ajoute(mon_contenu);

// OK
Base& b = mon_conteneur.get(0);

// ERREUR
Contenu<int>& c =
static_cast<Contenu<int> >(mon_conteneur.get(0));

return 1;
}

Bien sûr, ça ne marche pas puisque je ne peux retrouver le type
d'origine. Je vois comment faire avec une alternative "dynamique",
mais en statique, je ne vois pas.

Merci.

Fred

10 réponses

1 2
Avatar
Benoit Rousseau
Frédéric Mayot wrote:
Bonjour,

Soit une classe template Contenu :

template <typename T>
class Contenu {T t;};

Je cherche à réaliser une classe Conteneur pour des objets Contenu avec
des paramètres de template différents.

A présent, j'ai écrit ceci :

class Base{};

template <typename T>
class Contenu : public Base {T t;};

class Conteneur
{
protected:
std::vector<Base> objets;
Ca n'ira pas : tu ne pourras inserer que des Bases et pas des Contenu.

Il faut que tu utilises std::vector<Base*> ou un pointeur intelligent
sur Base*

public:
template <typename T>
void ajoute(T& b) {objets.push_back(b);}
Ici il y a un problème : tu ajoutes un objet de type T à un conteneur de

Base
Ou bien change le protype : void Ajoute( Base* b );

Base& get(int pos) {return objets[pos];}
};

int main(int argv, char** argc)
{
Conteneur mon_conteneur;
Contenu<int> mon_contenu;
Base* mon_contenu = new Contenu<int>;

mon_conteneur.ajoute(mon_contenu);

// OK
Base& b = mon_conteneur.get(0);

// ERREUR
Contenu<int>& c > static_cast<Contenu<int> >(mon_conteneur.get(0));

return 1;
}

Bien sûr, ça ne marche pas puisque je ne peux retrouver le type
d'origine. Je vois comment faire avec une alternative "dynamique",
mais en statique, je ne vois pas.

Merci.

Fred




Ces premières modifications devrait déjà te faire avancer

--
--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Vincent Lascaux
Quel est l'interet d'un tel conteneur ?

--
Vincent
Avatar
Benoit Rousseau
Vianney Lançon wrote:
A la rigueur tu te fais ta propre fonction de cast qui fait un
dynamic_cast + assert en debug et un static_cast en release.


Pas bête ça... C'est souvent utilisé ?


--------------------------------------------
Benoît Rousseau : roussebe at spray dot se
Jouez en programmant : http://realtimebattle.sourceforge.net/

Avatar
Vianney Lançon
Le Wed, 10 Dec 2003 10:43:18 +0100, dans fr.comp.lang.c++,
Frédéric Mayot a dit :
Bonjour,

Soit une classe template Contenu :



| template <typename T>
| class Contenu {T t;};


Je cherche à réaliser une classe Conteneur pour des objets Contenu avec
des paramètres de template différents.

A présent, j'ai écrit ceci :


|
| class Base{};
|
| template <typename T>
| class Contenu : public Base {T t;};
|
| class Conteneur
| {
| protected:

// Bombe atomique sur le point d'exploser à la ligne du dessous.
| std::vector<Base> objets;
| public:
| template <typename T>
| void ajoute(T& b) {objets.push_back(b);}
| Base& get(int pos) {return objets[pos];}
| };
|
| int main(int argv, char** argc)
| {
| Conteneur mon_conteneur;
| Contenu<int> mon_contenu;
| mon_conteneur.ajoute(mon_contenu);
|
| // OK
| Base& b = mon_conteneur.get(0);
|
| // ERREUR
/*
| Contenu<int>& c | static_cast<Contenu<int> >(mon_conteneur.get(0));
*/
Contenu<int>& c static_cast<Contenu<int>& >(mon_conteneur.get(0));
|
| return 1;
| }
|

Perso je ferais un std::vector<Base*> objects et un get qui retourne un
pointeur. Ce qui est facilitera le dynamic_cast (dans ce cas ajoute
un destructeur virtuel dans Base). Au passage vérifies l'index passé.
Par ce que la tu recopies un Contenu<int> dans un Base et
comme sizof(Base) < sizeof(Contenu<int>) tu auras des problèmes.

A la rigueur tu te fais ta propre fonction de cast qui fait un
dynamic_cast + assert en debug et un static_cast en release.

Enfin tu n'oublies pas dans ~Conteneur de libérer objects grâce
par example à une fonction template.
template <class StlContainer>
void release_stl_ptr_container(StlContainer & container);


--
Vianney LANÇON
radix omnia malorum prematurae optimisatia est
-- Donald Knuth

Avatar
Frédéric Mayot
Vincent Lascaux wrote:
Quel est l'interet d'un tel conteneur ?



Appliquer des opérations sur tous les membres. Si j'ai une méthode
Calcul dans Contenu, j'aurais par exemple Calcul_tout dans Conteneur qui
fera une itération sur la collection et appelera la méthode Calcul de
chaque objet Contenu.

Avatar
Frédéric Mayot
Perso je ferais un std::vector<Base*> objects et un get qui retourne un
pointeur. Ce qui est facilitera le dynamic_cast (dans ce cas ajoute
un destructeur virtuel dans Base).


Ca j'y avais pensé évidemment. Je voulais éviter des allocations
dynamiques (au tout du moins, laisser cela à mon conteneur vector).

Par ce que la tu recopies un Contenu<int> dans un Base et
comme sizof(Base) < sizeof(Contenu<int>) tu auras des problèmes.


C'est ce que je me suis dis aussi.

A la rigueur tu te fais ta propre fonction de cast qui fait un
dynamic_cast + assert en debug et un static_cast en release.


Tu peux expliciter un peu, je ne te suis pas très bien...

Fred

Avatar
Vincent Lascaux
Appliquer des opérations sur tous les membres. Si j'ai une méthode
Calcul dans Contenu, j'aurais par exemple Calcul_tout dans Conteneur qui
fera une itération sur la collection et appelera la méthode Calcul de
chaque objet Contenu.


Donc tout tes objets dérivent d'une classe de base, et tu n'as normalement
pas trop de cast à faire...

--
Vincent

Avatar
Vianney Lançon
Le Wed, 10 Dec 2003 11:52:01 +0100, dans fr.comp.lang.c++,
Frédéric Mayot a dit :
A la rigueur tu te fais ta propre fonction de cast qui fait un
dynamic_cast + assert en debug et un static_cast en release.


Tu peux expliciter un peu, je ne te suis pas très bien...


Tu as une hierachie avec Base qui est abstrait ainsi que Derived1 et Derived2
qui sont concret et qui derive de Base.
Et tu un containeur de Base* qui peuvent avoir pour type static soit
Derived1 soit Derived2.

dynamic_cast<Derived1*>(base) retourne 0 si base a pour type static
Derived2 sinon une addresse valide.

En géneral quand tu fait du downcasting tu fais

assert(base);
Derived1* derived = dynamic_cast<Derived1*>(base);
assert(derived);
return derived;

Mais cette operation prend un peu de temps (souvent négligeable).

Donc souvant les programeurs C font des casts plus ou moins sauvage
en ce disant que comme il connaisse le type final cette étape est
superflue.

Donc il font directement un static_cast.

La macro NDEBUG t'indique si tu es en mode debug. Tu peux donc optimiser
les fonctions critiques.



--
Vianney LANÇON
radix omnia malorum prematurae optimisatia est
-- Donald Knuth


Avatar
Vianney Lançon
Le Wed, 10 Dec 2003 11:35:02 +0100, dans fr.comp.lang.c++,
Benoit Rousseau a dit :
Vianney Lançon wrote:
A la rigueur tu te fais ta propre fonction de cast qui fait un
dynamic_cast + assert en debug et un static_cast en release.


Pas bête ça... C'est souvent utilisé ?


Dans le milieu professionel?
Ca dépend. un static_cast n'est pas strictement indentique
à un static_cast (exemple héritages multiples, virtuels
Et un professionnel n'aime pas que du code en release ait
un comportement different du code en debug. Il est plus compliqué
de trouver un bug qui ne se reproduit qu'en release.
Et pour l'optimisation, il est plus rentable de se concenter
sur les goulets d'etranglement.


Sur du code ou il y a déjà un static_cast ou un reinterpret_cast
et que l'on a des complexes à mettre un dynamic_cast car on se dit que
ça sert un rien.
Tu ajoutes un petit down_cast<>(), tu obtiens alors un code plus robuste
et tu perds tes complexes.


--
Vianney LANÇON
radix omnia malorum prematurae optimisatia est
-- Donald Knuth


Avatar
Marc Boyer
Frédéric Mayot wrote:
Bien sûr, ça ne marche pas puisque je ne peux retrouver le type
d'origine. Je vois comment faire avec une alternative "dynamique",
mais en statique, je ne vois pas.


Disons qu'il y a à mon humble avis un problème de
conception. C++ est un système à typage statique, qui
offre un certain polymorphisme dynamique à travers
des références ou des pointeurs uniquement.

Vouloir mettre dans un conteneur STL des données
de type différent sans utiliser de pointeur ou
de référence, c'est comme vouloir planter une vis
avec un clou. On doit pouvoir y arriver, mais à quel
prix, et en se tapant souvent sur les doigts avant
d'y arriver.
Au fait, pourquoi ne pas faire un conteneur avec
des références ? Le coût de l'indirection serait
vraiment prohibitif ?

Marc Boyer
--
Lying for having sex or lying for making war? Trust US presidents :-(

1 2