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();
}
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();
}
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.
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).
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>();
}
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.
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
}
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
}
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
.
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.
Starting with C++20, you can constrain template arguments. One way to do that is the requires
clause. You can use it as follows in your case (i.e. to ensure T
is a subclass of a specific class Baseclass
):
template <class T> void function()
requires( std::is_base_of_v<Baseclass,T> )
{
T *object = new T();
}
Alternatively, for frequently used constraints, you can use named concepts, which allow for a more succinct syntax (credit to @sklott):
template <std::derived_from<Baseclass> T> void function()
{
T *object = new T();
}
Using constraints is better than using a static_assert
, because they'll allow you to have multiple implementations of function
with different constraints. For example, you can have another implementation for when T
derives from a different class. If desired, you can also have a default implementation that doesn't specify any constraints at all, to be picked by the compiler when none of the constrained implementations qualify. None of these would be possible using static_assert
.
PS. The requires
clause is great for ad hoc constraints, and named concepts are great for frequently used constraints (you can also create your own named concepts using the concept
keyword). You can read more about constraints and alternative ways to apply them here, about std::is_base_of_v
here, and about std::derived_from
here. For more advanced use cases, also be sure to read about requires expressions, which are not to be confused with the requires clause, even though they share the same requires
keyword.
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()
{
//...
}
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.