Search code examples
c++templatesspecialization

Template specialization based on inherit class


I want to make this specialized w/o changing main. Is it possible to specialize something based on its base class? I hope so.

-edit-

I'll have several classes that inherit from SomeTag. I don't want to write the same specialization for each of them.

class SomeTag {};
class InheritSomeTag : public SomeTag {};

template <class T, class Tag=T>
struct MyClass
{
};

template <class T>
struct MyClass<T, SomeTag>
{
    typedef int isSpecialized;
};

int main()
{
    MyClass<SomeTag>::isSpecialized test1; //ok
    MyClass<InheritSomeTag>::isSpecialized test2; //how do i make this specialized w/o changing main()
    return 0;
}

Solution

  • This article describes a neat trick: http://www.gotw.ca/publications/mxc++-item-4.htm

    Here's the basic idea. You first need an IsDerivedFrom class (this provides runtime and compile-time checking):

    template<typename D, typename B>
    class IsDerivedFrom
    {
      class No { };
      class Yes { No no[3]; }; 
    
      static Yes Test( B* ); // not defined
      static No Test( ... ); // not defined 
    
      static void Constraints(D* p) { B* pb = p; pb = p; } 
    
    public:
      enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) }; 
    
      IsDerivedFrom() { void(*p)(D*) = Constraints; }
    };
    

    Then your MyClass needs an implementation that's potentially specialized:

    template<typename T, int>
    class MyClassImpl
    {
      // general case: T is not derived from SomeTag
    }; 
    
    template<typename T>
    class MyClassImpl<T, 1>
    {
      // T is derived from SomeTag
      public:
         typedef int isSpecialized;
    }; 
    

    and MyClass actually looks like:

    template<typename T>
    class MyClass: public MyClassImpl<T, IsDerivedFrom<T, SomeTag>::Is>
    {
    };
    

    Then your main will be fine the way it is:

    int main()
    {
        MyClass<SomeTag>::isSpecialized test1; //ok
        MyClass<InheritSomeTag>::isSpecialized test2; //ok also
        return 0;
    }