2009-05-06 7 views
3

Donc, un modèle de problème que je continue à rencontrer et que je n'ai pas une bonne solution est comment fournir des spécialisations de modèle qui sont basées sur le type duquel un paramètre de modèle est dérivé. Par exemple, supposons que je:Modèle pour spécialiser des modèles basés sur l'héritage possible?

template<typename T> 
struct implementPersist; 

template<typename T> 
void persist(T& object) 
{ 
    implementPersist::doPersist(object); 
} 

Ce que je voudrais est pour les utilisateurs de obstinent à être en mesure de fournir des implémentations de implementPersist :: persist pour les types qui sont déclarés après ce qui précède. C'est en principe simple, mais encombrant en pratique mais l'utilisateur doit fournir un implementPersist pour chaque type.

Pour être plus clair, supposons que je:

struct Persistent { virtual void myPersist() = 0; }; 
struct MyClass : public persistent { virtual void MyPersist() { ...implementation...} }; 

// Persists subclasses of Persistent using myPersist 
template<> 
struct implementPersist<Persistent>{ void doPersist(Persistent& p) { p->myPersist(); } }; 

struct X{}; 

template<> 
struct implementPersist<X>{ void doPersist(X& p) { ...implementation...} }; 


// Persists subclasses of Persistent using boostPersist 
struct MyBoostPersistedObject { virtual void boostPersist() = 0 }; 
struct Z : public MyBoostPersistedObject { virtual void boostPersist() = 0 }; 

template<> 
struct implementPersist<myBoostPersistedObject>{ void boostPersist() { ...implementation... } }; 

Mon intention est que je fournir une implémentation de modèle pour toutes les sous-classes de Persister, un autre pour toutes les sous-classes de myBoostPersistedObject et d'autres pour les classes diverses non structures de classes intéressantes (par exemple différents types de POD). Dans la pratique cependant,

implementPersist<Persistent>::doPersist 

est jamais si invoquaient :: persist (T &) est appelée où T est exactement un persistant objet. Cela revient au cas générique (manquant) où T = myClass. En général, je veux être capable de spécialiser des modèles de manière générique en fonction de l'héritage. C'est un peu frustrant parce que les compilateurs savent comment faire cela et le font lorsqu'ils décident d'appeler des fonctions en fonction de paramètres, par ex.

void persist (persistant &); persister les vides (X &); void persist (myBoostPersistedObject &); Mais autant que je sache, aucune correspondance similaire ne peut être faite pour les modèles.

Une solution de contournement est de faire quelque chose comme:

class persist; 

template<typename T, bool hasMyPersistMethod=isDerivedFrom(T,persist)::value > 
struct implementPersist; 

template<typename T, bool true > 
struct implementPersist<T,true> 
{ 
    template<> struct implementPersist<X>{ void doPersist(T& p) { p->myPersist(); } } 
}; 

(voir here pour isDerivedFrom). Cependant, cela nécessite que la déclaration initiale de implementPersist connaisse les types de classes fournissant les implémentations. J'aimerais quelque chose de plus générique.

Je trouve fréquemment des utilisations pour un tel modèle, afin d'éviter d'ajouter des spécialisations explicites pour chaque classe de mon système.

Des idées?

+0

Je ne peux pas expliquer pourquoi votre compilateur ne fera pas l'inférence de type que vous voulez. Cependant, mon expérience est compatible avec la vôtre dans le sens où les modèles et l'héritage ne semblent pas très bien jouer ensemble. –

Répondre

3

Oui, vous pouvez le faire en utilisant enable_if.

#include <iostream> 
#include <boost/type_traits.hpp> 
using namespace std; 


template <bool Enable, typename T = void> 
struct enable_if 
{ 
    typedef T type; 
}; 

template <typename T> 
struct enable_if<false, T> 
{ 
}; 

template <typename T, typename Enable = void> 
struct persist_t {}; 

struct A 
{ 
    virtual void foo() const = 0; 
}; 

template <typename T> 
struct persist_t<T, typename enable_if<boost::is_base_of<A, T>::value>::type> 
{ 
    static void persist(T const& x) 
    { 
     x.foo(); 
    } 
}; 


struct B : A 
{ 
    virtual void foo() const { cout << "B::foo\n"; } 
}; 

template <typename T> 
void persist(T & x) 
{ 
    persist_t<T>::persist(x); 
} 

int main() 
{ 
    B b; 
    persist(b); 
} 

Boost a une plus belle mise en œuvre de enable_if, je viens à condition ici pour être complet. Boost a aussi un exemple d'utilisation qui est très similaire à mon exemple ci-dessus.

Espérons que cela aide.

+0

Cette utilisation de enable_if ne semble pas correcte. Vous l'auriez normalement comme type de retour (ou comme paramètre par défaut redondant dans un constructeur). Avez-vous un lien vers l'exemple de boost similaire? –

+0

Oui, cliquez sur enable_if ci-dessus. Voici de toute façon: http://www.boost.org/doc/libs/1%5F39%5F0/libs/utility/enable%5Fif.html – Yuyo

+0

Merci - je ne savais pas à propos de cette utilisation –

0

Essayez d'utiliser une référence du type de base pour faire référence au type dérivé:

MyClass x(...); 
Persistent * p = &x; 
implementPersist<Persistent> ip; 
ip.doPersist(*p); 
Questions connexes