97

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

}
phant0m
  • 16,595
  • 5
  • 50
  • 82
  • 3
    What are you trying to accomplish by doing this? – sth Jul 04 '10 at 15:45
  • 4
    I just want to make sure that T is actually an instance of a subclass or the class itself. The code inside the function that I have provided is pretty much irrelevant. – phant0m Jul 04 '10 at 15:58
  • 7
    on the contrary, it's very relevant. It determines whether it's a good idea or not to put work into that test. In many (all?) cases, there is absolutely no need to enforce such constrains yourself, but rather let the compiler do it when instantiating. For example, for the accepted answer, it would be good to put a check on whether `T` derived from `Baseclass`. As of now, that check is implicit, and is not visible to overload resolution. But if nowhere such an implicit constraint is done, there appears to be no reason for an artificial restriction. – Johannes Schaub - litb Jul 04 '10 at 16:15
  • 3
    Yes, I agree. However, I just wanted to know whether there is a way to accomplish this or not :) But of course, you have a very valid point and thanks for the insight. – phant0m Jul 04 '10 at 16:27

8 Answers8

93

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.

Vish Desai
  • 1,141
  • 7
  • 8
61

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).

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • ah yes, that's a good idea. thanks! I take it then there's no way of defining it in the template definition? – phant0m Jul 04 '10 at 15:55
  • 3
    @phant0m: Correct. You can't explicitly constrain template parameters (except using concepts, which were considered for c++0x but then dropped). All constraints happen implicitly by the operations you perform on it (or in other words the only constraint is "The type must support all operations that are performed on it"). – sepp2k Jul 04 '10 at 16:00
  • 11
    That executes the T() constructor, and requires the existence of the T() constructor. See my answer for a way that avoids those requirements. – Douglas Leeder Jul 05 '10 at 09:15
  • @Douglas: The call to the `T()` constructor is already in the original code. I only changed the type of the `object` variable, which doesn't introduce any additional requirement except the one we were after. – sepp2k Jul 05 '10 at 12:03
  • 3
    Nice and clear, but this is a problem if T is a "heavy" class. – 3Dave Jan 08 '14 at 20:32
  • You could also do: BaseClass* bc = (BaseClass*)(T*)0; – Wheezil Nov 16 '15 at 18:44
  • This will also not compile if the `T` doesn't have default constructor, which is doesn't have to. – Tomáš Zato Jul 28 '16 at 12:34
55

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>();
}
Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
  • 2
    For me, this is the best and most interesting answer. Be sure to check Stroustrup's FAQ to read more about all kinds of constraints you could enforce similarly to this. – Jean-Philippe Pellet Mar 08 '13 at 23:32
  • 3
    Indeed, this is one hell of an answer! Thanks. The site mentioned is moved here: http://www.stroustrup.com/bs_faq2.html#constraints – Jan Korous Apr 03 '13 at 13:51
  • This is a great answer. Are there any good ways to avoid the warnings `unused variable 'p'` and `unused variable 'pb'`? – Filip S. Mar 26 '18 at 12:48
  • @FilipS. add `(void)pb;` after `B* pb = p;`. – bit2shift Dec 29 '18 at 10:09
21

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.

Community
  • 1
  • 1
justinpc
  • 797
  • 6
  • 19
11

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.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • What if you wrap all functions like this? btw what does it return? – the_drow Jul 04 '10 at 20:51
  • The `enable_if` takes a second type parameter that defaults to `void`. The expression `enable_if< true, int >::type` represents the type `int`. I cannot really understand what your first question is, you can use SFINAE for whatever you wish, but I don't quite understand what you intend to do with this over all functions. – David Rodríguez - dribeas Jul 04 '10 at 21:36
  • For those who wondered [what SFINAE is](https://en.cppreference.com/w/cpp/language/sfinae) :) – Matthieu Mar 02 '22 at 13:23
8

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.

Arda
  • 757
  • 10
  • 18
  • When using concepts, it can be even more short, like: `template T> void function()`. There is usually no need to use type traits with concepts, since there is usually exists concept for each type trait. – sklott Jan 25 '23 at 06:37
  • I prefer the requires clause, but you're right, I should have mentioned concepts. I've edited the answer to include information about them. – Arda Jan 25 '23 at 16:44
  • Concepts can be used with `requires` also: `template void function() requires std::derived_from` – sklott Jan 25 '23 at 22:09
  • That's correct; that's why I gave the link for "alternative ways to apply" constraints. This sentence from the link sums it up the best: *"In this case, the keyword `requires` must be followed by some constant expression (so it's possible to write `requires true`), but the intent is that a named concept (as in the example above) or a conjunction/disjunction of named concepts or a requires expression is used."* – Arda Jan 25 '23 at 23:07
4

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()
{
    //...
}
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
0

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.

DanDan
  • 10,462
  • 8
  • 53
  • 69
  • 5
    This does not ensure that `T` *is a* `BaseClass` because the declared members in `BaseClass` could be repeated in the declaration of `T`. – Daniel Trebbien Jul 04 '10 at 16:11