26

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

5 Answers 5

25

Update for concepts, using C++-20:

#include <concepts> struct NotSomeTag { }; struct SomeTag { }; struct InheritSomeTag : SomeTag { }; template<typename T> concept ConceptSomeTag = std::is_base_of_v<SomeTag, T>; template<class T> struct MyClass { }; // Specialization. template<ConceptSomeTag ST> struct MyClass<ST> { using isSpecialized = int; }; int main() { MyClass<SomeTag>::isSpecialized test1; /* ok */ MyClass<InheritSomeTag>::isSpecialized test2; /* ok */ MyClass<NotSomeTag>::isSpecialized test3; /* fail */ } 

My post from 2014, using C++-11:

#include <type_traits> struct SomeTag { }; struct InheritSomeTag : SomeTag { }; template<typename T, bool = std::is_base_of<SomeTag, T>::value> struct MyClass { }; template<typename T> struct MyClass<T, true> { typedef int isSpecialized; }; int main() { MyClass<SomeTag>::isSpecialized test1; /* ok */ MyClass<InheritSomeTag>::isSpecialized test2; /* ok */ } 
Sign up to request clarification or add additional context in comments.

4 Comments

Mind updating for C++17?
@Zelta Nothing was added to C++17 that is related to this. I'd still use the same code.
As a nit, in C++17 you can replace std::is_base_of<SomeTag, T>::value with std::is_base_of_v<SomeTag, T>.
@marcot I updated my answer after your comment (adding this comment just to keep your comment in context).
23

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

1 Comment

Beautiful. I have just a single, really stupid question — is there a reason that class Yes is declared as No no[3] rather than, say, No no[2]? I suppose that would work as well but I may be missing something important...
21

Well, the article in the answer above appeared in February 2002. While it works, today we know there are better ways. Alternatively, you can use enable_if:

template<bool C, typename T = void> struct enable_if { typedef T type; }; template<typename T> struct enable_if<false, T> { }; template<typename, typename> struct is_same { static bool const value = false; }; template<typename A> struct is_same<A, A> { static bool const value = true; }; template<typename B, typename D> struct is_base_of { static D * create_d(); static char (& chk(B *))[1]; static char (& chk(...))[2]; static bool const value = sizeof chk(create_d()) == 1 && !is_same<B volatile const, void volatile const>::value; }; struct SomeTag { }; struct InheritSomeTag : SomeTag { }; template<typename T, typename = void> struct MyClass { /* T not derived from SomeTag */ }; template<typename T> struct MyClass<T, typename enable_if<is_base_of<SomeTag, T>::value>::type> { typedef int isSpecialized; }; int main() { MyClass<SomeTag>::isSpecialized test1; /* ok */ MyClass<InheritSomeTag>::isSpecialized test2; /* ok */ } 

3 Comments

Man, I completely forgot about SFINAE when I wrote my answer. Still, I'm letting it stand to show a weird alternative.
@litb: Although, defining the util structs enable_if and is_base_of is redundant if one can use TR1; but thanks for doing so here, helped me understand :)
boost also has this functionality built in: enable_if and is_base_of. (This was useful for me to find out since I was already using boost in my package; maybe it will be useful to someone else as well).
4

In your case, the only way that I see would be to explicitly specialize MyClass for InheritSomeTag. However, the SeqAn paper proposes a mechanism called “template sublassing” that does what you want – albeit with a different inheritance syntax, so the code isn't compatible with your current main function.

// Base class template <typename TSpec = void> class SomeTag { }; // Type tag, NOT part of the inheritance chain template <typename TSpec = void> struct InheritSomeTag { }; // Derived class, uses type tag template <typename TSpec> class SomeTag<InheritSomeTag<TSpec> > : public SomeTag<void> { }; template <class T, class Tag=T> struct MyClass { }; template <class T, typename TSpec> struct MyClass<T, SomeTag<TSpec> > { typedef int isSpecialized; }; int main() { MyClass<SomeTag<> >::isSpecialized test1; //ok MyClass<SomeTag<InheritSomeTag<> > >::isSpecialized test2; //ok } 

This certainly looks strange and is very cumbersome but it allows a true inheritance mechanism with polymorphic functions that is executed at compile time. If you want to see this in action, have a look at some SeqAn examples.

That being said, I believe that SeqAn is a special case and not many applications would profit from this extremely difficult syntax (deciphering SeqAn-related compiler errors is a real pain in the *ss!)

Comments

1

Using concepts and the requires keyword from C++20 is an even simpler and more expressive way to do this without having to introduce a redundant boolean non-type template parameter like in C++11:

// C++20: #include <concepts> #include <iostream> struct SomeTag { }; struct InheritSomeTag : SomeTag { }; template<typename T> struct MyClass { void Print() { std::cout << "Not derived from someTag\n"; } }; // std::derived_from is a predefined concept already included in the STL template<typename T> requires std::derived_from<T, SomeTag> struct MyClass<T> { void Print() { std::cout << "derived from someTag\n"; } }; int main() { MyClass<InheritSomeTag> test1; test1.Print(); // derived from someTag MyClass<int> test2; test2.Print(); // Not derived from someTag // Note how even the base tag itself returns true from std::derived_from: MyClass<SomeTag> test3; test3.Print(); // derived from someTag } 

Comments