Changjian YU
Changjian YU

Reputation: 63

How to elegantly check if the template type is derived from the specific base class in C++11?

I have a template class Context. I want to limit the user to use the specified type (Stratege1 but not Stratege2) that is derived from a specific class Base.

class Base {
public: 
    virtual void run() = 0;
}; 
class Stratege1 : public Base {
public:
    virtual void run() {
        printf("Stratege1 \n");
    }
}; 
class Stratege2 {
public:
    virtual void run() {
        printf("Stratege2 \n"); 
    }
}; 
template <typename T> class Context {
public:
    void run() { 
        t.run();
    }; 
private:
    T t;
};

It may be OK if the user want to invoke like this:

Context<Stratege1> context;
context.run();

However I don't expect the user use (to avoid unexpectedly potential run-time issues)

Context<Stratege2> context;
context.run();

Because Stratege2 is not derived from Base class. Is there any elegant way to limit the concept during compilation?

Thanks for any suggestions.

Upvotes: 3

Views: 1562

Answers (3)

YSC
YSC

Reputation: 40150

Since C++11, you can static_assert something, this means get a nice compilation error when a compile-time check fails:

#include <type_traits> // is_base_of
template <typename T>
class Context {
    static_assert(std::is_base_of<Base, T>::value,
        "T must be a derived class of Base in Context<T>.");
public:
    void run() { 
        t.run();
    }; 
private:
    T t;
};

For instance:

Context<NotBase> c2;
error: static_assert failed "T must be a derived class of Base in Context<T>."
    -> static_assert(std::is_base_of<Base, T>::value,
note: in instantiation of template class 'Context<NotBase>' requested here
    -> Context<NotBase> c2;

Full program demo

Upvotes: 5

max66
max66

Reputation: 66230

Is there any elegant way to limit the concept during compilation?

Another possible solution is through partial specialization: the second template paramenter is true only if T is derived from base

template <typename T, bool = std::is_base_of<Base, T>::value>
class Context;

template <typename T>
class Context<T, true>
 {
   private:
      T t;

   public:
      void run () { t.run(); }; 
 };

So you have

Context<Stratege1>  cs1;    // compile
// Context<Stratege2>  cs2; // compilation error

Unfortunately you can hijack Context explicating the second parameter

Context<Stratege2, true> cs2; // compile

Upvotes: 1

Poeta Kodu
Poeta Kodu

Reputation: 1160

Use std::enable_if_t (equivalent to std::enable_if<B,T>::type) and std::is_base_of.

#include <type_traits>

template <typename T,
    typename = std::enable_if_t<std::is_base_of<Base, T>::value> >
class Context {
public:
    void run() { 
        t.run();
    }; 
private:
    T t;
};

Upvotes: 3

Related Questions