Restrict C++ Template Parameter to Subclass

C++Templates

C++ Problem Overview


How can I force a template parameter T to be a subclass of a specific class Baseclass? Something like this:

template <class T : Baseclass> void function(){
    T *object = new T();

}

C++ Solutions


Solution 1 - C++

With a C++11 compliant compiler, you can do something like this:

template<class Derived> class MyClass {

    MyClass() {
        // Compile-time sanity check
        static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");

        // Do other construction related stuff...
        ...
   }
}

I've tested this out using the gcc 4.8.1 compiler inside a CYGWIN environment - so it should work in *nix environments as well.

Solution 2 - C++

In this case you can do:

template <class T> void function(){
    Baseclass *object = new T();

}

This will not compile if T is not a subclass of Baseclass (or T is Baseclass).

Solution 3 - C++

To execute less useless code at runtime you can look at: http://www.stroustrup.com/bs_faq2.html#constraints which provides some classes that perform the compile time test efficiently, and produce nicer error messages.

In particular:

template<class T, class B> struct Derived_from {
		static void constraints(T* p) { B* pb = p; }
		Derived_from() { void(*p)(T*) = constraints; }
};

template<class T> void function() {
    Derived_from<T,Baseclass>();
}

Solution 4 - C++

Since C++11 you do not need Boost or static_assert. C++11 introduces is_base_of and enable_if. C++14 introduces the convenience type enable_if_t, but if you are stuck with C++11, you can simply use enable_if::type instead.

Alternative 1

David Rodríguez's solution may be rewritten as follows:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}
Alternative 2

Since C++17, we have is_base_of_v. The solution can be further rewritten to:

#include <type_traits>

using namespace std;

template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}
Alternative 3

You could also just restrict the the whole template. You could use this method for defining whole classes. Note how the second parameter of enable_if_t has been removed (it was previously set to void). Its default value is actually void, but it doesn't matter, as we are not using it.

#include <type_traits>

using namespace std;

template <typename T,
          typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

From the documentation of template parameters, we see that typename = enable_if_t... is a template parameter with an empty name. We are simply using it to ensure that a type's definition exists. In particular, enable_if_t will not be defined if Base is not a base of T.

The technique above is given as an example in enable_if.

Solution 5 - C++

You don't need concepts, but you can use SFINAE:

template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function() {
   // This function will only be considered by the compiler if
   // T actualy derived from Base
}

Note that this will instantiate the function only when the condition is met, but it will not provide a sensible error if the condition is not met.

Solution 6 - C++

You could use Boost Concept Check's BOOST_CONCEPT_REQUIRES:

#include <boost/concept_check.hpp>
#include <boost/concept/requires.hpp>

template <class T>
BOOST_CONCEPT_REQUIRES(
    ((boost::Convertible<T, BaseClass>)),
(void)) function()
{
    //...
}

Solution 7 - C++

By calling functions inside your template that exist in the base class.

If you try and instantiate your template with a type that does not have access to this function, you will receive a compile-time error.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionphant0mView Question on Stackoverflow
Solution 1 - C++Vish DesaiView Answer on Stackoverflow
Solution 2 - C++sepp2kView Answer on Stackoverflow
Solution 3 - C++Douglas LeederView Answer on Stackoverflow
Solution 4 - C++justinpcView Answer on Stackoverflow
Solution 5 - C++David Rodríguez - dribeasView Answer on Stackoverflow
Solution 6 - C++Daniel TrebbienView Answer on Stackoverflow
Solution 7 - C++DanDanView Answer on Stackoverflow