OVH Cloud OVH Cloud

Re: Héritage et template

14 réponses
Avatar
Christophe Lephay
Michaël Monerau" <cort@meloo.com> a écrit dans le message de
news:pGXdb.181652$hd6.2309645@news.chello.at...
> Christophe Lephay wrote:
> >> template <class T>
> >> class UVar
> >> {
> >> public:
> >> typedef const_ref_type const T&;
> >>
> >> virtual xxx GetValue () const = 0;
> >> // je voudrais :
> >> virtual const T& GetValue () const = 0;
> >> };
> >
> > J'ai un jour été confronté à ce problème, et je l'ai résolu en
> > passant par un stream pour extraire la valeur...
>
> C'est-à-dire ? Je ne vois pas ce que tu veux dire... Mon but est aussi de
> passer par un stream ensuite pour l'afficher. Le type 'T' doit alors aussi
> surcharger l'opérateur '<<' pour lui...
>
> Mais si tu fais passer directement par un stream, comment récupérer
> l'information si tu ne sais pas de quel type elle est ?

La méthode classique...

class base
{
protected:
virtual void print( ostream& ostr ) = 0;
};

ostream& operator<<( ostream& ostr, base& b )
{
base.print( ostr );
return ostr;
}

template< class T >
class derived : public base
{
public:
derived( const T& param ) : value( param ) {}

protected:
void print( ostream& ostr )
{
ostr << value;
}

private:
T value;
};

Chris


PS : désolé si le message apparait dans un nouveau thread, mais mon OE fait
encore des siennes...

10 réponses

1 2
Avatar
Christophe Lephay
"Christophe Lephay" a écrit dans le message
de news:bl9rjp$8k7$
ostream& operator<<( ostream& ostr, base& b )
{
base.print( ostr );
return ostr;
}



Oups, vous avez mal lu ;)

Il fallait lire :

b.print( ostr );

Chris

Avatar
Michaël Monerau
Christophe Lephay wrote:
La méthode classique...

class base
{
protected:
virtual void print( ostream& ostr ) = 0;
};

ostream& operator<<( ostream& ostr, base& b )
{
base.print( ostr );
return ostr;
}

template< class T >
class derived : public base
{
public:
derived( const T& param ) : value( param ) {}

protected:
void print( ostream& ostr )
{
ostr << value;
}

private:
T value;
};


Ok, mais là c'est seulement pour le mettre dans un string. Admettons que
maintenant, je veuille récupérer la valeur contenue dans derived, avec un
pointeur vers base... Est-ce possible en C++ ?
--
<=- Michaël "Cortex" Monerau -=>

Avatar
Christophe Lephay
"Michaël Monerau" a écrit dans le message de
news:AB%db.186235$
Christophe Lephay wrote:
La méthode classique...

class base
{
protected:
virtual void print( ostream& ostr ) = 0;
};

ostream& operator<<( ostream& ostr, base& b )
{
base.print( ostr );
return ostr;
}

template< class T >
class derived : public base
{
public:
derived( const T& param ) : value( param ) {}

protected:
void print( ostream& ostr )
{
ostr << value;
}

private:
T value;
};


Ok, mais là c'est seulement pour le mettre dans un string.


Dans un stream, pas un string...

Admettons que
maintenant, je veuille récupérer la valeur contenue dans derived, avec un
pointeur vers base... Est-ce possible en C++ ?


L'opération est totalement symétrique. Il suffit de définir de la même
manière istream& operator>>( istream&, base& ) et une fonction membre
virtual void get( istream& )...

Chris


Avatar
Christophe Lephay
"Christophe Lephay" a écrit dans le message
de news:bladm6$3hh$
"Michaël Monerau" a écrit dans le message de
Admettons que
maintenant, je veuille récupérer la valeur contenue dans derived, avec
un


pointeur vers base... Est-ce possible en C++ ?


L'opération est totalement symétrique. Il suffit de définir de la même
manière istream& operator>>( istream&, base& ) et une fonction membre
virtual void get( istream& )...


Hum, après relecture, je me demande si j'ai bien compris ta question...

La question à laquelle je répondais est "est-il possible de récupérer la
valeur injectée dans le flux pour la transmettre à un objet à travers un
pointeur ou une référence sur sa classe de base".

Que veux-tu dire par "récupérer la valeur" ?

Par exemple :

derived< int > iderived ( 10 );
base * pbase = &iderived;

iostream str;
str << *pbase; // tu injectes 10 dans le flux

iderived = 0; // il faudrait definir operator= pour celà

str >> *pbase; // on extrait le 10 pour le remettre dans iderived

Chris


Avatar
Michaël Monerau
Christophe Lephay wrote:
Que veux-tu dire par "récupérer la valeur" ?


Je veux dire, récupérer le membre valeur de derived. Et c'est bien ce que ta
méthode fait :-)

Par exemple :

derived< int > iderived ( 10 );
base * pbase = &iderived;

iostream str;
str << *pbase; // tu injectes 10 dans le flux

iderived = 0; // il faudrait definir operator= pour celà

str >> *pbase; // on extrait le 10 pour le remettre dans iderived


En effet. Ca marche très bien ! Avec un membre virtuel 'get (ostream&)', on
peut très bien y arriver... Merci pour cette solution.

Cependant, au niveau perfs, je pense qu'il serait préférable d'optimiser la
chose sans créer un nouveau iostream à chaque fois (opération coûteuse comme
on l'a vu), mais de réutiliser le même (oss.str("")).

Mais je ne sais pas si iostream a la même fonctionnalité (str ("")) que
ostream... Je suppose que oui puisqu'il y a dérivation de ostream, mais ça
mérite une vérification de ma part, quand je rentrerai du lycée :-D
--
<=- Michaël "Cortex" Monerau -=>

Avatar
Loïc Joly
Michaël Monerau wrote:

Christophe Lephay wrote:

La méthode classique...

class base;

template< class T >
class derived : public base


Ok, mais là c'est seulement pour le mettre dans un string. Admettons que
maintenant, je veuille récupérer la valeur contenue dans derived, avec un
pointeur vers base... Est-ce possible en C++ ?


Tant que tu veux la "récupérer" d'une façon générique qui ne dépend pas
du type, et que tout ton code est templaté sur le type, la question ne
se pose pas vraiment.

Si maintenant tu veux à un moment faire une opération spécifique au type
(par exemple, si tu veux diviser deux derived<double> mais pas deux
derived<string>), il va falloir déterminer ce qu'est vraiment ton type.

Tu dois pouvoir faire ça soit par downcast explicite (dynamic_cast),
soit par détermination du type par surcharge de fonction (tu as un
f(string), un f(double)... et tu appèles f(T)).

--
Loïc


Avatar
Christophe Lephay
"Michaël Monerau" a écrit dans le message de
news:5I8eb.193946$
En effet. Ca marche très bien ! Avec un membre virtuel 'get (ostream&)',
on

peut très bien y arriver... Merci pour cette solution.

Cependant, au niveau perfs, je pense qu'il serait préférable d'optimiser
la

chose sans créer un nouveau iostream à chaque fois (opération coûteuse
comme

on l'a vu), mais de réutiliser le même (oss.str("")).


Pourquoi pas un membre static de la classe de base ?

Chris

Avatar
Michaël Monerau
Christophe Lephay wrote:
"Michaël Monerau" a écrit dans le message de
news:5I8eb.193946$
En effet. Ca marche très bien ! Avec un membre virtuel 'get
(ostream&)', on peut très bien y arriver... Merci pour cette
solution.

Cependant, au niveau perfs, je pense qu'il serait préférable
d'optimiser la chose sans créer un nouveau iostream à chaque fois
(opération coûteuse comme on l'a vu), mais de réutiliser le même
(oss.str("")).


Pourquoi pas un membre static de la classe de base ?


C'est ce à quoi je pensais quand je disais "réutiliser le même". En effet,
c'est une option assez sympa :-)
--
<=- Michaël "Cortex" Monerau -=>


Avatar
Michaël Monerau
Christophe Lephay wrote:
Que veux-tu dire par "récupérer la valeur" ?


En relisant de plus près les posts (et en essayant d'imaginer le code), je
me suis rendu compte que j'avais mal compris ta solution.

Elle ne correspond pas à ce que je veux, puisqu'on revient avec un pointeur
base*. Or, moi, je voudrais la *valeur* qui est réellement contenue par la
base. Mais c'est en fait impossible je pense, sauf en passant par ma méthode
de cast sauvage et unsafe...

Ce qui serait intéressant dans ce cas, serait d'avoir des typedefs virtuels.
Je m'explique : imaginons qu'on puisse écrire ça :

class base
{
public:
virtual typedef value_type void; // ne sera pas utilisé

virtual value_type* get ();

//...
};

template <class T>
class derive : public base
{
public:
virtual typedef value_type T;

virtual value_type* get ();

//...
};

Du coup, on pourrait avoir la valeur en fonction de son type. Mais dans la
pratique, on ne peut pas changer le type de retour d'une fonction virtuelle,
donc ça ne marche pas. Enfin, il suffirait de pouvoir le passer de void* en
un autre type. Comme ça, on déréférencerait directement dans le bon type
même depuis un pointeur vers base*. (tiens, ce n'est pas le but d'auto ça ?
je n'ai pas suivi le débat sur celui-ci mais il me semble que ça s'en
rapproche, non ?).

et on pourrait récupérer la valeur par un :

pBase->value_type Valeur = pBase->get();

C'est bien sûr utopique, mais ça me paraîtrait bien pratique dans mon cas...
Enfin, ça doit sûrement entraîner d'autres effets de bord que je ne vois pas
;-)
--
<=- Michaël "Cortex" Monerau -=>

Avatar
Michaël Monerau
Loïc Joly wrote:
Michaël Monerau wrote:

Christophe Lephay wrote:

La méthode classique...

class base;

template< class T >
class derived : public base


Ok, mais là c'est seulement pour le mettre dans un string. Admettons
que maintenant, je veuille récupérer la valeur contenue dans
derived, avec un pointeur vers base... Est-ce possible en C++ ?


Tant que tu veux la "récupérer" d'une façon générique qui ne dépend
pas du type, et que tout ton code est templaté sur le type, la
question ne se pose pas vraiment.


Oui, mais la base n'est pas templatée. Du coup, comment déterminer le retour
de get() ? Un void* que je recaste dans le bon type (ce que je fais dans ma
fonction). Mais après, comment déterminer le bon type depuis seulement un
pointeur vers base* ? Pour l'instant, je n'ai pas trouvé et je fais
confiance à l'utilisateur, ce qui peut vite devenir la merde, sauf si j'ai
des typedef's pour tous les noms d'options...

Si maintenant tu veux à un moment faire une opération spécifique au
type (par exemple, si tu veux diviser deux derived<double> mais pas
deux derived<string>), il va falloir déterminer ce qu'est vraiment
ton type.

Tu dois pouvoir faire ça soit par downcast explicite (dynamic_cast),
soit par détermination du type par surcharge de fonction (tu as un
f(string), un f(double)... et tu appèles f(T)).


Oui, mais ça restreint... Je voudrais vraiment que cette classe puisse
prendre n'importe quelle type comme valeur. (voir réponse à James que je
vais écrire :p).
--
<=- Michaël "Cortex" Monerau -=>



1 2