Reputation: 63
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
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;
Upvotes: 5
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
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