8

Given is a class MyClass with one template parameter

template<typename T>
class MyClass
{
    //...
};

and another class MySecondClass with two template parameters.

template<typename T, typename U>
class MySecondClass
{
    //...
};

What I would like to do is to restrict MyClass to only allow a T that is a derived type of MySecondClass. I already know I need something like

template<typename T, typename = std::enable_if<std::is_base_of<MySecondClass<?,?>, T>::value>>
class MyClass
{
    //...
}

I am just not sure what to put in for the ? as I want to allow all possible MySecondClass's.

user1056903
  • 921
  • 9
  • 26
  • I edited the question. If you feel that it is not correct please roll it back – NathanOliver Aug 22 '16 at 13:39
  • I wonder about the point of the exercise. How do you plan to use the fact that `T` is derived from some instantiation of `MyParentClass`? What do you believe you can do with `T` knowing this, that you couldn't do otherwise? – Igor Tandetnik Aug 22 '16 at 13:41
  • 2
    why do you keep changing the question? – m.s. Aug 22 '16 at 13:58
  • @IgorTandetnik I want to search for an object of a subclass `X` of `MySecondClass` in a Vector and store a reference to this object in the instantiation of `MyClass` which can be then accessed with a getter `X getMySecondClassChild()` in `MyClass`. – user1056903 Aug 22 '16 at 15:23
  • No part of your explanation appears to require that `X` be derived from `MySecondClass`. The only connection thereto is the name of the method, `getMySecondClassChild`. Call it `getX` instead, and `MySecondClass` is completely out of the picture. – Igor Tandetnik Aug 22 '16 at 23:46
  • MySecondClass is a abstract class (with several pure virtual functions). All subclasses X of MySecondClass should implement these pure virtual functions and inherit already implemented functions. Im MyClass I want to enforce that the getter always returns a X. Perhaps I should create a new question for this design problems... – user1056903 Aug 23 '16 at 09:17

2 Answers2

7

You can use a template template parameter for the base template, then check if a T* can be converted to some Temp<Args...>:

template <template <typename...> class Of, typename T>
struct is_base_instantiation_of {
    template <typename... Args>
    static std::true_type test (Of<Args...>*);
    static std::false_type test (...);

    using type = decltype(test(std::declval<T*>()));
    static constexpr auto value = type::value;
};

Live Demo

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Your live demo fails to compile... – Cornstalks Aug 22 '16 at 13:54
  • 4
    @Cornstalks I believe that is the point as `int` don't derive from `MyParentClass` template class (hence `enable_if_t` fails and makes sure `MyClass` is not part of the overload resolution for `MyClass`). – dfrib Aug 22 '16 at 13:56
  • @dfri: Ah, you're right. I misread it. A comment on the two lines indicating that "compilation will fail here" would have helped make it clear the failure is intentional. – Cornstalks Aug 22 '16 at 13:58
  • @TartanLlama: Yes, thank you! – Cornstalks Aug 22 '16 at 14:16
4

You can use a custom trait to check whether a type is derived from a template. Then use this trait inside a static_assert:

#include <type_traits>

template <template <typename...> class T, typename U>
struct is_derived_from_template
{
private:
    template <typename... Args>
    static decltype(static_cast<const T<Args...>&>(std::declval<U>()), std::true_type{}) test(
            const T<Args...>&);
    static std::false_type test(...);

public:
    static constexpr bool value = decltype(test(std::declval<U>()))::value;
};


template <typename T1, typename T2>
struct MyParentClass
{
};

template<typename T>
struct MyClass
{
    static_assert(is_derived_from_template<MyParentClass, T>::value, "T must derive from MyParentClass");
};


struct DerivedFromMyParentClass : MyParentClass<int, float>{};

struct Foo{};

int main()
{
    MyClass<DerivedFromMyParentClass> m;
    MyClass<Foo> f;
}

live example

m.s.
  • 16,063
  • 7
  • 53
  • 88