Spécialisation d'un modèle vers modèle utilisant des pointeurs

Spécialisation d'un modèle vers modèle utilisant des pointeurs - C++ - Programmation

Marsh Posté le 15-03-2007 à 18:40:37    

Bonjour,
   Je me suis posé un petit exercice : j'ai d'abord cherché à créer un modèle de conteneur associatif.
Ce qui me donne :  

Code :
  1. #include<vector>
  2. #include<algorithm>
  3. template<typename T, typename T2>
  4. struct Pair
  5. {
  6. T key;
  7. T2 value;
  8. Pair(T gkey):key(gkey){};
  9. Pair(T gkey, T2 gvalue):key(gkey), value(gvalue) {};
  10. bool operator==(const Pair<T, T2> &other);
  11. };
  12. template<typename T, typename T2>
  13. bool Pair<T, T2>::operator ==(const Pair<T,T2> &other)
  14. {
  15. return (other.key == key);
  16. }
  17. template<typename T, typename T2>
  18. class Map
  19. {
  20. std::vector<Pair<T, T2>> data;
  21. public:
  22. typedef typename std::vector<Pair<T,T2>>::iterator iterator;
  23. iterator begin();
  24. iterator end();
  25. virtual T2 &operator[](T key);
  26. };
  27. template<typename T, typename T2>
  28. T2 &Map<T, T2>::operator [](T key)
  29. {
  30. std::vector<Pair<T, T2>>::iterator it = data.begin();
  31. it = std::find(data.begin(), data.end(), Pair<T,T2>(key, T2()));
  32. if(it == data.end())
  33. {
  34.  data.push_back(Pair<T,T2>(key, T2()));
  35.  return (data.back()).value;
  36. }
  37. else
  38.  return (*it).value;
  39. }
  40. template<typename T, typename T2>
  41. typename Map<T,T2>::iterator Map<T, T2>::begin()
  42. {
  43. return data.begin();
  44. }
  45. template<typename T, typename T2>
  46. typename Map<T,T2>::iterator Map<T, T2>::end()
  47. {
  48. return data.end();
  49. }


Donc ça, j'ai pas de soucis : ça marche. Mais a titre d'exercice je me suis dit que j'allais chercher a traiter autrement les pointeurs. Donc pour cela j'ai dérivé mon modèle comme ça :

Code :
  1. template<typename T, typename T2>
  2. class Map<T, T2*> : public Map<T, T2>
  3. {
  4. public:
  5. T2 *operator[](T key);
  6. };
  7. template<typename T, typename T2>
  8. T2 *Map<T, T2*>::operator [](T key)
  9. {
  10. std::vector<Pair<T, T2>>::iterator it = data.begin();
  11. it = std::find(data.begin(), data.end(), Pair<T,T2>(key));
  12. if(it == data.end())
  13. {
  14.  data.push_back(Pair<T,T2*>(key, &T2()));
  15.  return (data.back()).value;
  16. }
  17. else
  18.  return (*it).value;
  19. }


Mais la, mon problème c'est qu'il est pas content que le type de retour de mon opérateur [] change entre la classe mère et la classe fille. Ce que je comprend... mais en fait je vois pas comment régler le problème. (autre que de changer d'exemple pour m'entrainer à ce type de spécialisation... :D )
 
Donc, je sais pas si vous voyez une solution...
Merci :)
 
ps : au passage si il y a quelque chose qui vous choque dans mon utilisation des modèles, merci de me le signaler (je demande qu'a m'améliorer :) )

Reply

Marsh Posté le 15-03-2007 à 18:40:37   

Reply

Marsh Posté le 16-03-2007 à 08:45:59    

Bon ... y a des trucs à revoir :|
 
Deja ton schéma d'héritage entre modèle me  
parait pas la meilleur solution. Ensuite, tu  
cherche à spécialiser partiellement une méthode  
de ton modèle dans le cas ou T2 est un pointeur.  
Pour cela, il te faut résoudre 2 problèmes :
 
-> générer un type de retour valide
-> changer le contenu de ta méthode.
 
Une maniére plus propre consiste à :
 
1/ Génération du type de retour :
On utilise une technique qui s'appelle les Traits.  
Cette technique consiste à utiliser des spécialisations  
de template comme un filtre logique (un match en ML).  
Ici on a deux cas :
 
T2 quelconque -> on renvoit T2&
T2* -> on renvoit T2*
 
 
En outre, lorsque tu crée T2& , il faut faire gaffe au fait  
que T2 ne soit pas déjà une référence ! On crée donc le modèle  
suivant  
 

Code :
  1. template<class T> struct return_type
  2. {
  3.    typedef T& type;
  4. };
  5. template<class T> struct return_type<T&>
  6. {
  7.    typedef T& type;
  8. };
  9. template<class T> struct return_type<T*>
  10. {
  11.    typedef T* type;
  12. };


 
On peut désormais récupérer le type de retour de manière géénrique.
 
2/ Modifier le contenu de ta méthode
Même constat. On va exporter le code dabs un modèle ad-hoc qui sera appelé depuis ton modèle.
 
 
On crée donc un modèle qui ne contient que ton code de recherche dnas chaque cas.
 

Code :
  1. template<class T, class T2>
  2. struct map_access
  3. {
  4.   typedef typename return_type<T2>::type type;
  5.   typedef std::vector<Pair<T,T2>>        map_type;
  6.   static inline type run( const T& key, const map_type& data )
  7.   {
  8.     typename map_type::iterator it = data.begin();
  9.     it = std::find(data.begin(), data.end(), Pair<T,T2>(key, T2()));
  10.     if(it == data.end())
  11.     {
  12.       data.push_back(Pair<T,T2>(key, T2()));
  13.       return (data.back()).value;
  14.     }
  15.     else
  16.       return (*it).value;
  17.   }
  18. };
  19. template<class T, class T2>
  20. struct map_access<T,T2*>
  21. {
  22.   typedef typename return_type<T2*>::type type;
  23.   typedef std::vector<Pair<T,T2*>>        map_type;
  24.   static inline type run( const T& key, const map_type& data )
  25.   {
  26.     typename map_type::iterator it = data.begin();
  27.     it = std::find(data.begin(), data.end(), Pair<T,T2*>(key));
  28.     if(it == data.end())
  29.     {
  30.        data.push_back(Pair<T,T2*>(key, &T2()));
  31.        return (data.back()).value;
  32.     }
  33.     else
  34.       return (*it).value;
  35.   }
  36. };


 
Au final le code de Map<T,T2>::operator[] devient
 

Code :
  1. template<typename T, typename T2>
  2. typename return_type<T2>::type Map<T, T2>::operator [](T key)
  3. {
  4.   return map_access<T,T2>::run(key,data);
  5. }


 
Voila le principe.
 
Ensuite quelques conseils :
 
1/ std::map pue du bec que tu ai envie d'en réécrire un moins bien ?
2/ Y a plein de problème de const qui manque partout ...
3/ Il faut garder à l"esprit que lorsque tu écris du code à base de template, il faut que la PLUS GROSSE PARTIE du code manipulant des types soit générique. T2& c'ets pas générique, return_type l'est beaucoup plus car tu contrôle précisement tes schémas de spécialisation.
4/ utilise le plus possible les modéles de BOOST::Traits et de BOOST::mpl pour te simplifier la vie sur ce genre de tests


Message édité par Joel F le 16-03-2007 à 09:00:10
Reply

Marsh Posté le 16-03-2007 à 12:30:37    

Merci, beaucoup de ton aide !
 
   Bon, je vais m'entrainer encore mais j'ai compris le principe  
   bien sur, il est clair que c'est pas que la std::map "pue du bec" que j'ai essayé d'en faire une mais en fait c'est un exercice qui étais proposé dans mon Stroustrup. Je viens de lire le chapitre sur les modèles et donc je fais ensuite les exercices
 
Donc je vais améliorer tout ça... Merci pour tes conseils et de m'avoir montré le principe qu'il faut utiliser :)

Reply

Marsh Posté le 16-03-2007 à 12:33:53    

ouaich, vive les traits (que y en a plein de défini en standard déjà)

Reply

Marsh Posté le 16-03-2007 à 19:03:44    

Taz a écrit :

ouaich, vive les traits (que y en a plein de défini en standard déjà)


mmouuais. C'est dans TR1, et ce sera probablement au prochain standard, mais c'est surclassé par les concepts.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed