Pregunta

Quiero especializar parcialmente una plantilla existente que no puedo cambiar (std::tr1::hash) para una clase base y todas las clases derivadas. La razón es que estoy usando el patrón de plantilla curiosamente recurrente para el polimorfismo, y la función hash se implementa en la clase base CRTP. Si solo quiero especializarme parcialmente para una clase base CRTP, entonces es fácil, simplemente puedo escribir:


namespace std { namespace tr1 {

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

} }

Pero esta especialización no coincide con las clases derivadas reales, solo CRTPBase<Derived>. Lo que quiero es una forma de escribir una especialización parcial para Derived si y solo si se deriva de enable_if<condition, Derived>::type. Mi pseudocódigo es


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(); 
    }
};

} }

... pero eso no funciona porque el compilador no puede decir que boost::enable_if es enable_if. Si pudiera cambiar unordered_set, simplemente agregaría otro parámetro de plantilla ficticia para usar unordered_map, como lo recomienda la documentación hash, pero obviamente esa no es una muy buena solución. ¿Hay alguna forma de evitar este problema? ¿Tengo que especificar una plantilla hash personalizada en cada <=> o <=> que creo, o especializarme completamente en <=> para cada clase derivada?

¿Fue útil?

Solución

Hay dos variantes en el siguiente código. Puedes elegir más apropiado para ti.


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

}

Otros consejos

En lugar de modificar std::tr1::hash, debe crear su propio espacio de nombres y definir allí una nueva estructura hash que herede de CRTPBase<Derived> o esté especializada para <=>.


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

}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top