Definition d'une interface (a la Java) a l'aide de templates
62 réponses
Aurélien REGAT-BARREL
Bonjour à tous,
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble
de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites
de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc
rabatu sur les templates.
Le but est de traduire un shéma UML où divers objets implémentent une
interface ITriplet qui stipule que :
- l'objet est construit à partir de 3 valeurs A, B et C (=> un triplet)
- l'objet possède une méthode GetValue(int) permettant d'accéder à ces 3
valeurs
- l'objet possède une méthode statique GetTripletName() qui retourne le nom
du triplet
Le truc étant que la plupart des objets font des modifications sur A, B et C
avant de les stocker. Les divers objets qui implémentent ITriplet n'ont rien
en commun et ne doivent pas être comparables, pas mélangeables, ... d'où pas
de polymorphisme.
Voilà les solutions auxquelles j'ai pensé, quel est votre opinion :
Pour l'exemple, un seul objet ABC inmplémente ITriplet.
1- on laisse le programmeur faire tout ce qu'il faut (on le guide au
maximum)
INTERFACE( ITriplet
{
public:
ITriplet( double A, double B, double C );
double GetValue( int Index ) const;
static std::string GetTripletName();
};
)
// triplet ABC
class ABC IMPLEMENTS( ITriplet )
{
public:
ABC( double A, double B, double C )
{
data.reserve( 3 );
data.push_back( A );
data.push_back( B );
data.push_back( C );
}
double GetValue( int Index ) const
{
return this->data.at( Index );
}
static std::string GetTripletName()
{
return "A.B.C";
}
private:
std::vector<double> data;
};
template <typename T>
WHERE( T : ITriplet )
T TripletBuilder()
{
return T( 0, 1, 2 );
}
int main()
{
ABC abc = TripletBuilder<ABC>();
}
2- on a bien une interface ITriplet sous forme de template. C'est plus
l'aspect macro qui est exploité ici
// functor pour construire le triplet
typedef std::vector<double> (TripletCtor)( double A, double B, double C );
// functor pour récupérer son nom
typedef std::string (TripletGetName)();
// interface ITriplet
template<TripletCtor Func, TripletGetName Name>
class ITriplet
{
public:
ITriplet( double A, double B, double C ) :
data( Func( A, B, C ) )
{
}
double GetValue( int Index ) const
{
return this->data.at( Index );
}
static std::string GetTripletName()
{
return Name();
}
private:
std::vector<double> data;
};
// triplet ABC
std::vector<double> ComputeABC( double A, double B, double C )
{
std::vector<double> result;
result.reserve( 3 );
result.push_back( A );
result.push_back( B );
result.push_back( C );
return result;
}
std::string GetNameABC()
{
return "A.B.C";
}
typedef ITriplet<ComputeABC, GetNameABC> ABC;
3 - fournir un template de vérification des méthodes attendues. Au lieu de
donner une interface au programmeur comme en 1, on donne un test à faire
passer à son objet.
template<typename T>
class ITripletImplementationChecker
{
public:
ITripletImplementationChecker()
{
double a = 0.0;
double b = 0.0;
double c = 0.0;
T t( a, b, c );
t.GetValue( 0 );
t.GetValue( 1 );
t.GetValue( 2 );
T::GetTripletName();
}
};
ITripletImplementationChecker<ABC> check_abc;
Comme j'ai l'impression de faire n'importe quoi, j'aimerais avoir votre
avis.
Merci de votre aide.
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... C'est comme faire (pour ce que j'ai compris) :
class A; class B : public A { }; class A { };
Plutôt
class B;
class A {
// Utilisation de B type incomplet
};
class B: A {};
sauf que là ça marche... pour moi les templates ça marche un peu comme les macros ( ~ génération de code),
Tu compare une expression règulière à une grammaire (attribuée).
mais apparement c'est pas le cas. J'ai toujours pas compris :-/ --
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au
début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être
instancié avec ABC, or ABC n'est pas encore implémenté...
C'est comme faire (pour ce que j'ai compris) :
class A;
class B : public A
{
};
class A
{
};
Plutôt
class B;
class A {
// Utilisation de B type incomplet
};
class B: A {};
sauf que là ça marche... pour moi les templates ça marche un peu comme les
macros ( ~ génération de code),
Tu compare une expression règulière à une grammaire (attribuée).
mais apparement c'est pas le cas. J'ai toujours pas compris :-/
--
Du coup je pige pas pourquoi ton exemple fonctionne
Parce que dans
class ABC: public ITriplet<ABC> ...
quand tu écris "ITriplet<ABC>", la classe ABC a déjà été déclarée (au début de la ligne).
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... C'est comme faire (pour ce que j'ai compris) :
class A; class B : public A { }; class A { };
Plutôt
class B;
class A {
// Utilisation de B type incomplet
};
class B: A {};
sauf que là ça marche... pour moi les templates ça marche un peu comme les macros ( ~ génération de code),
Tu compare une expression règulière à une grammaire (attribuée).
mais apparement c'est pas le cas. J'ai toujours pas compris :-/ --
Franck Branjonneau
Franck Branjonneau
Loïc Joly écrivait:
Fabien LE LEZ wrote:
On Mon, 12 Jul 2004 15:44:36 +0200, "Aurélien REGAT-BARREL" :
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... En fait, je ne sais pas du tout si c'est légal, mais ça a l'air de
marcher. Y a-t-il quelqu'un dans la salle qui sache comment fonctionnent les templates ?
Je sais pas trop comment, mais en tout cas, je sais que ça marche. Ca porte même un nom : CRTP ( Curiously Recurring Template Pattern). Regarde par exemple : http://www.informit.com/articles/article.asp?p1473&seqNum=3
(c'est extrait du livre de Jossutis et Vandevoorde sur les templates)
(Je suis fatigué -- vérifier les conneries que je raconte)
Quand on écrit
template< typename > struct U {
};
struct V: U< V > {
};
Il y a /instanciation implicite/ de U avec le paramètre V car hériter d'une classe nécessite que cette classe soit définie. Le /point d'instanciation/ se situe avant "l'enclosing namespace" du machin qui à déclencher l'instanciation. -- Franck Branjonneau
On Mon, 12 Jul 2004 15:44:36 +0200, "Aurélien REGAT-BARREL"
<nospam-aregatba@yahoo.fr.invalid>:
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être
instancié avec ABC, or ABC n'est pas encore implémenté...
En fait, je ne sais pas du tout si c'est légal, mais ça a l'air de
marcher. Y a-t-il quelqu'un dans la salle qui sache comment
fonctionnent les templates ?
Je sais pas trop comment, mais en tout cas, je sais que ça marche. Ca
porte même un nom : CRTP ( Curiously Recurring Template
Pattern). Regarde par exemple :
http://www.informit.com/articles/article.asp?p1473&seqNum=3
(c'est extrait du livre de Jossutis et Vandevoorde sur les templates)
(Je suis fatigué -- vérifier les conneries que je raconte)
Quand on écrit
template< typename >
struct U {
};
struct V: U< V > {
};
Il y a /instanciation implicite/ de U avec le paramètre V car hériter
d'une classe nécessite que cette classe soit définie.
Le /point d'instanciation/ se situe avant "l'enclosing namespace"
du machin qui à déclencher l'instanciation.
--
Franck Branjonneau <fasbjx@free.fr>
On Mon, 12 Jul 2004 15:44:36 +0200, "Aurélien REGAT-BARREL" :
Oui, mais quand tu écris ITriplet<ABC>, le template ITriplet va être instancié avec ABC, or ABC n'est pas encore implémenté... En fait, je ne sais pas du tout si c'est légal, mais ça a l'air de
marcher. Y a-t-il quelqu'un dans la salle qui sache comment fonctionnent les templates ?
Je sais pas trop comment, mais en tout cas, je sais que ça marche. Ca porte même un nom : CRTP ( Curiously Recurring Template Pattern). Regarde par exemple : http://www.informit.com/articles/article.asp?p1473&seqNum=3
(c'est extrait du livre de Jossutis et Vandevoorde sur les templates)
(Je suis fatigué -- vérifier les conneries que je raconte)
Quand on écrit
template< typename > struct U {
};
struct V: U< V > {
};
Il y a /instanciation implicite/ de U avec le paramètre V car hériter d'une classe nécessite que cette classe soit définie. Le /point d'instanciation/ se situe avant "l'enclosing namespace" du machin qui à déclencher l'instanciation. -- Franck Branjonneau
Franck Branjonneau
drkm écrivait:
template < typename T > class TripletDefaultStorage { protected: TripletDefaultStorage( T first , T second , T third ) { myDatas[ 0 ] = first ; myDatas[ 1 ] = second ; myDatas[ 2 ] = third ; } T getValue( int index ) const { return myDatas[ index ] ; } private: T myDatas[ 3 ] ; } ;
Pourquoi un getter et pas un simple triplet à la std::pair<> ? D'autant que tous les membres sont protégés. Si le getter se justifie pourquoi pas une fonction avec un argument template ? Quelque chose comme
tout en assurant une vérification at compile time. -- Franck Branjonneau
drkm <usenet.fclcxx@fgeorges.org> écrivait:
template < typename T >
class TripletDefaultStorage
{
protected:
TripletDefaultStorage(
T first , T second , T third
)
{
myDatas[ 0 ] = first ;
myDatas[ 1 ] = second ;
myDatas[ 2 ] = third ;
}
T getValue( int index ) const {
return myDatas[ index ] ;
}
private:
T myDatas[ 3 ] ;
} ;
Pourquoi un getter et pas un simple triplet à la std::pair<> ?
D'autant que tous les membres sont protégés.
Si le getter se justifie pourquoi pas une fonction avec un argument
template ? Quelque chose comme
template < typename T > class TripletDefaultStorage { protected: TripletDefaultStorage( T first , T second , T third ) { myDatas[ 0 ] = first ; myDatas[ 1 ] = second ; myDatas[ 2 ] = third ; } T getValue( int index ) const { return myDatas[ index ] ; } private: T myDatas[ 3 ] ; } ;
Pourquoi un getter et pas un simple triplet à la std::pair<> ? D'autant que tous les membres sont protégés. Si le getter se justifie pourquoi pas une fonction avec un argument template ? Quelque chose comme
Bonjour à tous, Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites
de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse. C'est + restrictif que le java, menfin...
Bonjour à tous,
Je voudrais traduire une interface au sens pur, vraiment logique (ensemble
de méthodes). Je ne veux pas passer par l'héritage et les classes
abstraites
de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc
rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une
interface en C++ si je ne m'abuse.
C'est + restrictif que le java, menfin...
Bonjour à tous, Je voudrais traduire une interface au sens pur, vraiment logique (ensemble de méthodes). Je ne veux pas passer par l'héritage et les classes abstraites
de bases, car notamment je ne veux pas de polymorphisme. Je me suis donc rabatu sur les templates.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse. C'est + restrictif que le java, menfin...
Franck Branjonneau
drkm écrivait:
Franck Branjonneau writes:
drkm écrivait:
Franck Branjonneau writes:
[ Des conneries. ]
Je précise que je n'ai pas du tout écrit cela :-O
C'est moi qui dit : j'ai écrit des conneries. -- Franck Branjonneau
drkm <usenet.fclcxx@fgeorges.org> écrivait:
Franck Branjonneau <fasbjx@free.fr> writes:
drkm <usenet.fclcxx@fgeorges.org> écrivait:
Franck Branjonneau <fasbjx@free.fr> writes:
[ Des conneries. ]
Je précise que je n'ai pas du tout écrit cela :-O
C'est moi qui dit : j'ai écrit des conneries.
--
Franck Branjonneau <fasbjx@free.fr>
C'est moi qui dit : j'ai écrit des conneries. -- Franck Branjonneau
drkm
Franck Branjonneau writes:
drkm écrivait:
Pourquoi un getter et pas un simple triplet à la std::pair<> ?
Parce que ça faisait partie de la spécification du PO, il me semble. Et que le but de la manoeuvre est justement d'encapsuler le stockage des valeurs. Si elles ne sont pas stockées conjointement dans toutes les implémentation de le politique, comme c'est le cas dans mon exemple, cela complique les choses.
D'autant que tous les membres sont protégés. Si le getter se justifie pourquoi pas une fonction avec un argument template ? Quelque chose comme
Bof. Qu'apporte le template ? Mais l'énumération n'est pas une mauvaise idée, si l'on sait que l'on ne manipulera que des triplets.
element_type getValue( Ordinal i ) { return myDatas[ i ] ; }
Bien que, ... Une énumération qui définit first = 0, second = 1 et third = 2 n'est pas très utile. Il est aussi clair d'utiliser les entiers directement.
Ce qui évite
class TripletDoCheck class TripletNoCheck
tout en assurant une vérification at compile time.
Et si les indices ne sont pas disponibles à la compilation ? Dans ce cas, on laisse le choix à l'utilisateur. Soit il se charge des vérifications, à chaque fois, ce qui peut être contraignant mais est peut-être déjà réalisé à un niveau supérieur.
Soit il délègue cette vérification à notre classe ; il doit juste le préciser à l'instantiation. Mais surtout, cela sert à lui permettre de spécifier le comportement à adopter en cas d'erreur : lancer une exception (quelle exception), arrêter le programme, donner une valeur par défaut (il suffit de créer une implémentation de la politique dont check() prend une référence non-constante), etc. Et cela en moins de 10 lignes.
Et s'il veut un comportement de politique modifiable à l'exécution, se basant sur l'héritage :
class MyCheckBase { public: virtual void doCheck( int i ) = 0 ; } ;
class MyCheckImpl1 { public: virtual void doCheck( int i ) { // ... } } ;
// ...
class MyCheck { public: MyCheckBase * myImpl ; MyCheck( MyCheckBase * c ) : myImpl( c ) { } void check( int i ) { myImpl->doCheck( i ) ; } } ;
On a pour cela besoin d'une interface étendue, au moins le champ en publique, comme ici. Mais comme le modèle hérite en publique de ses arguments, l'utilisateur peut étendre l'interface selon ses besoins.
--drkm
Franck Branjonneau <fasbjx@free.fr> writes:
drkm <usenet.fclcxx@fgeorges.org> écrivait:
Pourquoi un getter et pas un simple triplet à la std::pair<> ?
Parce que ça faisait partie de la spécification du PO, il me semble.
Et que le but de la manoeuvre est justement d'encapsuler le stockage
des valeurs. Si elles ne sont pas stockées conjointement dans toutes
les implémentation de le politique, comme c'est le cas dans mon
exemple, cela complique les choses.
D'autant que tous les membres sont protégés.
Si le getter se justifie pourquoi pas une fonction avec un argument
template ? Quelque chose comme
Bof. Qu'apporte le template ? Mais l'énumération n'est pas une
mauvaise idée, si l'on sait que l'on ne manipulera que des triplets.
element_type getValue( Ordinal i ) {
return myDatas[ i ] ;
}
Bien que, ... Une énumération qui définit first = 0, second = 1 et
third = 2 n'est pas très utile. Il est aussi clair d'utiliser les
entiers directement.
Ce qui évite
class TripletDoCheck
class TripletNoCheck
tout en assurant une vérification at compile time.
Et si les indices ne sont pas disponibles à la compilation ? Dans
ce cas, on laisse le choix à l'utilisateur. Soit il se charge des
vérifications, à chaque fois, ce qui peut être contraignant mais est
peut-être déjà réalisé à un niveau supérieur.
Soit il délègue cette vérification à notre classe ; il doit juste le
préciser à l'instantiation. Mais surtout, cela sert à lui permettre
de spécifier le comportement à adopter en cas d'erreur : lancer une
exception (quelle exception), arrêter le programme, donner une valeur
par défaut (il suffit de créer une implémentation de la politique dont
check() prend une référence non-constante), etc. Et cela en moins de
10 lignes.
Et s'il veut un comportement de politique modifiable à l'exécution,
se basant sur l'héritage :
class MyCheckBase
{
public:
virtual void doCheck( int i ) = 0 ;
} ;
class MyCheckImpl1
{
public:
virtual void doCheck( int i ) {
// ...
}
} ;
// ...
class MyCheck
{
public:
MyCheckBase * myImpl ;
MyCheck( MyCheckBase * c )
: myImpl( c )
{
}
void check( int i ) {
myImpl->doCheck( i ) ;
}
} ;
On a pour cela besoin d'une interface étendue, au moins le champ en
publique, comme ici. Mais comme le modèle hérite en publique de ses
arguments, l'utilisateur peut étendre l'interface selon ses besoins.
Pourquoi un getter et pas un simple triplet à la std::pair<> ?
Parce que ça faisait partie de la spécification du PO, il me semble. Et que le but de la manoeuvre est justement d'encapsuler le stockage des valeurs. Si elles ne sont pas stockées conjointement dans toutes les implémentation de le politique, comme c'est le cas dans mon exemple, cela complique les choses.
D'autant que tous les membres sont protégés. Si le getter se justifie pourquoi pas une fonction avec un argument template ? Quelque chose comme
Bof. Qu'apporte le template ? Mais l'énumération n'est pas une mauvaise idée, si l'on sait que l'on ne manipulera que des triplets.
element_type getValue( Ordinal i ) { return myDatas[ i ] ; }
Bien que, ... Une énumération qui définit first = 0, second = 1 et third = 2 n'est pas très utile. Il est aussi clair d'utiliser les entiers directement.
Ce qui évite
class TripletDoCheck class TripletNoCheck
tout en assurant une vérification at compile time.
Et si les indices ne sont pas disponibles à la compilation ? Dans ce cas, on laisse le choix à l'utilisateur. Soit il se charge des vérifications, à chaque fois, ce qui peut être contraignant mais est peut-être déjà réalisé à un niveau supérieur.
Soit il délègue cette vérification à notre classe ; il doit juste le préciser à l'instantiation. Mais surtout, cela sert à lui permettre de spécifier le comportement à adopter en cas d'erreur : lancer une exception (quelle exception), arrêter le programme, donner une valeur par défaut (il suffit de créer une implémentation de la politique dont check() prend une référence non-constante), etc. Et cela en moins de 10 lignes.
Et s'il veut un comportement de politique modifiable à l'exécution, se basant sur l'héritage :
class MyCheckBase { public: virtual void doCheck( int i ) = 0 ; } ;
class MyCheckImpl1 { public: virtual void doCheck( int i ) { // ... } } ;
// ...
class MyCheck { public: MyCheckBase * myImpl ; MyCheck( MyCheckBase * c ) : myImpl( c ) { } void check( int i ) { myImpl->doCheck( i ) ; } } ;
On a pour cela besoin d'une interface étendue, au moins le champ en publique, comme ici. Mais comme le modèle hérite en publique de ses arguments, l'utilisateur peut étendre l'interface selon ses besoins.
Yep. Je n'avais pas reçu cet article lorsque j'ai écrit le mien. Désolé.
--drkm
Aurélien REGAT-BARREL
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse. C'est + restrictif que le java, menfin...
Oui mais étant abstraite je peux pas créer un vecteur de triplets par exemples. Ou alors il faut passer par les pointeurs. Le problème de la gestion mémoire & du polymorphisme se posent alors. Je peux du coup mélanger des triplets dans un même tableau, ou comparer 2 triplets de types différents, ce qui n'a pas de sens. Je ne dois même pas avoir la possibilité de traiter un triplet au sens général : Affiche( const ITriplet & ); // ne doit pas être possible Pourquoi ? Parce que dans mon cas ça n'a pas de sens. On affiche un ABC, un DEF ou un GHI, mais pas un Triplet. Il est important que le programmeur explicite toujours le type de triplet manipulé. ITriplet n'est là que par commodité, c'est un détail d'implémentation. A un niveau d'abstraction plus élevé, il n'existe que ABC, DEF et GHI qui n'ont rien en commun, sinon le fait d'avoir 3 valeurs. Le problème : il faut écrire autant de fonctions qu'il y a de triplets : Affiche( const ABC & ); Affiche( const DEF & ); Affiche( const GHI & ); pareil et pire pour le calcul : ABC CalculeABC(); DEF CalculeDEF(); GHI CalculeGHI();
Avec les templates : ABC abc = Caclule<ABC>(); ABC def = Caclule<DEF>();
Affiche( abc ); Affiche( def );
J'ai une seule fonction calcule et affiche. Et pas possible de faire ITriplet * abc = new DEF( 0, 0, 0 ); Le typage des triplets doit être statique.
-- Aurélien REGAT-BARREL
Pourtant c'est la classe abstraite qui est le mode défini pour faire une
interface en C++ si je ne m'abuse.
C'est + restrictif que le java, menfin...
Oui mais étant abstraite je peux pas créer un vecteur de triplets par
exemples. Ou alors il faut passer par les pointeurs. Le problème de la
gestion mémoire & du polymorphisme se posent alors. Je peux du coup mélanger
des triplets dans un même tableau, ou comparer 2 triplets de types
différents, ce qui n'a pas de sens.
Je ne dois même pas avoir la possibilité de traiter un triplet au sens
général :
Affiche( const ITriplet & ); // ne doit pas être possible
Pourquoi ? Parce que dans mon cas ça n'a pas de sens. On affiche un ABC, un
DEF ou un GHI, mais pas un Triplet. Il est important que le programmeur
explicite toujours le type de triplet manipulé. ITriplet n'est là que par
commodité, c'est un détail d'implémentation. A un niveau d'abstraction plus
élevé, il n'existe que ABC, DEF et GHI qui n'ont rien en commun, sinon le
fait d'avoir 3 valeurs.
Le problème : il faut écrire autant de fonctions qu'il y a de triplets :
Affiche( const ABC & );
Affiche( const DEF & );
Affiche( const GHI & );
pareil et pire pour le calcul :
ABC CalculeABC();
DEF CalculeDEF();
GHI CalculeGHI();
Avec les templates :
ABC abc = Caclule<ABC>();
ABC def = Caclule<DEF>();
Affiche( abc );
Affiche( def );
J'ai une seule fonction calcule et affiche. Et pas possible de faire
ITriplet * abc = new DEF( 0, 0, 0 ); Le typage des triplets doit être
statique.
Pourtant c'est la classe abstraite qui est le mode défini pour faire une interface en C++ si je ne m'abuse. C'est + restrictif que le java, menfin...
Oui mais étant abstraite je peux pas créer un vecteur de triplets par exemples. Ou alors il faut passer par les pointeurs. Le problème de la gestion mémoire & du polymorphisme se posent alors. Je peux du coup mélanger des triplets dans un même tableau, ou comparer 2 triplets de types différents, ce qui n'a pas de sens. Je ne dois même pas avoir la possibilité de traiter un triplet au sens général : Affiche( const ITriplet & ); // ne doit pas être possible Pourquoi ? Parce que dans mon cas ça n'a pas de sens. On affiche un ABC, un DEF ou un GHI, mais pas un Triplet. Il est important que le programmeur explicite toujours le type de triplet manipulé. ITriplet n'est là que par commodité, c'est un détail d'implémentation. A un niveau d'abstraction plus élevé, il n'existe que ABC, DEF et GHI qui n'ont rien en commun, sinon le fait d'avoir 3 valeurs. Le problème : il faut écrire autant de fonctions qu'il y a de triplets : Affiche( const ABC & ); Affiche( const DEF & ); Affiche( const GHI & ); pareil et pire pour le calcul : ABC CalculeABC(); DEF CalculeDEF(); GHI CalculeGHI();
Avec les templates : ABC abc = Caclule<ABC>(); ABC def = Caclule<DEF>();
Affiche( abc ); Affiche( def );
J'ai une seule fonction calcule et affiche. Et pas possible de faire ITriplet * abc = new DEF( 0, 0, 0 ); Le typage des triplets doit être statique.