Assume the following situation:
Simplified example code could look like this:
#include <iostream>
#include <type_traits>
template <typename R, typename S>
class ICanDoIt
{
public:
virtual void doStuff() = 0;
protected:
ICanDoIt<R, S>(R rA, S sA) : r(rA), s(sA) {};
R r;
S s;
};
class DoesIt : public ICanDoIt<int, double>
{
public:
DoesIt(int iA, double dA) : ICanDoIt(iA, dA) {};
virtual void doStuff()
{ std::cout << "r * s = " << r * s << " done." << std::endl; }
};
template <typename T>
class NeedsSomeoneWhoCanDoIt
{
static_assert(std::is_base_of<ICanDoIt<R, S>, T>::value,
"T needs to be able to do it.");
public:
NeedsSomeoneWhoCanDoIt(const T& doesItA) : doesIt(doesItA) {};
void getItDone() { doesIt.doStuff(); };
private:
T doesIt;
};
int main()
{
DoesIt doesIt(5, 2.2);
NeedsSomeoneWhoCanDoIt<DoesIt> needsIt(doesIt);
needsIt.getItDone();
}
If you untemplate the interface "ICanDoIt" the code will actually work. But the static_assert for the templated version will fail because ICanDoIt's template arguments are wrapped and hidden by the specialization performed in the decalaration of DoesIt.
How can I limit the managing classes (NeedsSomeoneWhoCanDoIt) template parameter "T" to any specialization of ICanDoIt, regardless of which type has been chosen for R, S during the specialization of ICanDoIt?
You could always make the actual types for R
and S
used to instantiate ICanDoIt
accessible to a derived class, i.e.
template <typename R, typename S> class ICanDoIt {
public:
typedef R R_t;
typedef S S_t;
virtual void doStuff() = 0;
};
so that your static_assert
would become
static_assert(std::is_base_of<ICanDoIt<typename T::R_t, typename T::S_t>,
T>::value,
"T needs to be able to do it.");
Depending on what your actual code looks like the design might become clearer if you'd define a purely abstract base class (i.e. an actual type ICanDoItBase
instead of a template) from which you'd inherit the currently templated functionality in ICanDoIt
which would again be a base of DoesIt
.
NeedsSomeoneWhoCanDoIt
could then directly use the the polymorphic base class ICanDoItBase
without any needs for additional type checks.