Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to partially specialize a class template for all derived types?

I want to partially specialize an existing template that I cannot change (std::tr1::hash) for a base class and all derived classes. The reason is that I'm using the curiously-recurring template pattern for polymorphism, and the hash function is implemented in the CRTP base class. If I only want to partially specialize for a the CRTP base class, then it's easy, I can just write:


namespace std { namespace tr1 {

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

} }

But this specialization doesn't match actual derived classes, only CRTPBase<Derived>. What I want is a way of writing a partial specialization for Derived if and only if it derives from CRTPBase<Derived>. My pseudo-code is


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

} }

...but that doesn't work because the compiler can't tell that enable_if<condition, Derived>::type is Derived. If I could change std::tr1::hash, I'd just add another dummy template parameter to use boost::enable_if, as recommended by the enable_if documentation, but that's obviously not a very good solution. Is there a way around this problem? Do I have to specify a custom hash template on every unordered_set or unordered_map I create, or fully specialize hash for every derived class?

like image 584
Doug Avatar asked Jun 23 '09 14:06

Doug


People also ask

Can a template base class derived?

It is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.

When we specialize a function template it is called?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is the difference between a template and a class?

An individual class defines how a group of objects can be constructed, while a class template defines how a group of classes can be generated. Note the distinction between the terms class template and template class: Class template.

What is the use of template specialization?

This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming. Generic programming is an approach where generic data types are used as parameters in algorithms so that they work for variety of suitable data types.


1 Answers

There are two variants in the following code. You could choose more appropriated for you.


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

}
like image 78
Alexey Malistov Avatar answered Sep 19 '22 08:09

Alexey Malistov