Question

Je souhaite spécialiser partiellement un modèle existant que je ne peux pas modifier (std::tr1::hash) pour une classe de base et toutes les classes dérivées. La raison en est que j'utilise le modèle de modèle curieusement récurrent pour le polymorphisme et que la fonction de hachage est implémentée dans la classe de base CRTP. Si je veux seulement me spécialiser partiellement pour la classe de base CRTP, alors c'est facile, je peux simplement écrire:


namespace std { namespace tr1 {

template <typename Derived>
struct hash<CRTPBase<Derived> >
{
    size_t operator()(const CRTPBase<Derived> & base) const 
    { 
        return base.hash(); 
    }
};

} }

Mais cette spécialisation ne correspond pas aux classes dérivées réelles, mais uniquement CRTPBase<Derived>. Ce que je veux, c’est un moyen d’écrire une spécialisation partielle pour Derived si et seulement si elle dérive de enable_if<condition, Derived>::type. Mon pseudo-code est


namespace std { namespace tr1 {

template <typename Derived>
struct hash<typename boost::enable_if<std::tr1::is_base_of<CRTPBase<Derived>, Derived>,
    Derived>::type>
{
    size_t operator()(const CRTPBase<Derived> & base) const 
    { 
        return base.hash(); 
    }
};

} }

... mais cela ne fonctionne pas car le compilateur ne peut pas dire que boost::enable_if est enable_if. Si je pouvais changer unordered_set, j'ajouterais simplement un autre paramètre de modèle factice à utiliser unordered_map, comme recommandé par la hash documentation, mais ce n'est évidemment pas une très bonne solution. Y a-t-il un moyen de contourner ce problème? Dois-je spécifier un modèle de hachage personnalisé pour chaque <=> ou <=> je crée ou me spécialiser <=> pour chaque classe dérivée?

Était-ce utile?

La solution

Il existe deux variantes dans le code suivant. Vous pouvez choisir plus approprié pour vous.


template <typename Derived>
struct CRTPBase
{
    size_t hash() const {return 0; }
};

// First case 
//
// Help classes
struct DummyF1 {};
struct DummyF2 {};
struct DummyF3 {};
template<typename T> struct X; 

// Main classes
template<> struct X<DummyF1> : CRTPBase< X<DummyF1> > {
    int a1;
};

template<> struct X<DummyF2> : CRTPBase< X<DummyF2> > {
    int b1;
};

// typedefs
typedef X<DummyF1> F1;
typedef X<DummyF2> F2;
typedef DummyF3    F3; // Does not work

namespace std { namespace tr1 {
    template<class T>
    struct hash< X<T> > {
        size_t operator()(const CRTPBase< X<T> > & base) const     
        {         
            return base.hash();     
        }
    };
}} // namespace tr1 // namespace std 

//

// Second case
struct DummyS1 : CRTPBase <DummyS1> {
    int m1;
};
//
template<typename T> 
struct Y : T {};
//
typedef Y<DummyS1> S1;


namespace std { namespace tr1 {
    template<class T>
    struct hash< Y<T> > {
        size_t operator()(const CRTPBase<T> & base) const     
        {         
            return base.hash();     
        }
    };
}} // namespace tr1 // namespace std 

void main1()
{
    using std::tr1::hash;
    F1 f1;
    F2 f2;
    F3 f3;
    hash<F1> hf1; size_t v1 = hf1(f1); // custom hash functor
    hash<F2> hf2; size_t v2 = hf2(f2); // custom hash functor
    hash<F3> hf3; size_t v3 = hf3(f3); // error: standard hash functor

    S1 s1;
    hash<S1> hs1; size_t w1 = hs1(s1); // custom hash functor

}

Autres conseils

Au lieu de modifier std::tr1::hash, vous devez créer votre propre espace de nom et définir sa nouvelle structure hash qui hérite de CRTPBase<Derived> ou est spécialisée pour <=>.


template <typename Derived>
struct CRTPBase
{
    size_t hash() {return 0; }
};

struct AA : CRTPBase <AA> {};
struct BB {};
//
namespace mynamespace {

template <typename Some, typename Dummy=char> 
struct hash : std::tr1::hash<Some> {};
//
template <typename Derived>
struct hash<Derived, 
  typename boost::enable_if< std::tr1::is_base_of<CRTPBase<Derived>, Derived>, char>::type >
{    
    size_t operator()(const CRTPBase<Derived> & base) const     
    {         
        return base.hash();     
    }
};

} // namespace mynamespace {}
//
//
void ff()
{
    using namespace mynamespace;

    hash<AA> aa;  // my hash
    hash<BB> bb;  // std::tr1::hash

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top