7

I have written a small utility for testing whether or not a type has inherited some template instantiation of a specific template class, either directly or trough inheriting a class that inherits the template. This is accomplished with a SFINAE check using a template function accepting any template instantiation of the provided template and a fallback overload for the default case.

#include <iostream>
#include <type_traits>

template<template<class> class T, class U>
struct isDerivedFrom
{
    static constexpr bool value = decltype(isDerivedFrom::test(U()))::value;
private:
    template<class V>
    static std::true_type test(T<V>);
    static std::false_type test(...);
};

template<class T>
struct Base {};
struct Base_D1 : Base<int> {};
struct Base_D2 : Base<Base_D2> {};
struct Base_D1_D1 : Base_D1 {};
struct NotDerived {};

int main()
{
    std::cout << std::boolalpha
        << "is Base_D1 derived from or a template instantiation of Base: "
        << isDerivedFrom<Base, Base_D1>::value << "\n"
        << "is Base_D2 derived from or a template instantiation of Base: "
        << isDerivedFrom<Base, Base_D2>::value << "\n"
        << "is Base_D1_D1 derived from or a template instantiation of Base: "
        << isDerivedFrom<Base, Base_D1_D1>::value << "\n"
        << "is Base<double> derived from or a template instantiation of Base: "
        << isDerivedFrom<Base, Base<double>>::value << "\n"
        << "is NotDerived derived from or a template instantiation of Base: "
        << isDerivedFrom<Base, NotDerived>::value << "\n";
    return 0;
}

Output:

is Base_D1 derived from or a template instantiation of Base: true
is Base_D2 derived from or a template instantiation of Base: true
is Base_D1_D1 derived from or a template instantiation of Base: true
is Base<double> derived from or a template instantiation of Base: true
is NotDerived derived from or a template instantiation of Base: false

My problem is that if the type to be tested (template argument T of isDerivedFrom) has or inherits a non-public constructor or inherits the template trough non-public inheritance, it causes a compile error because decltype(T()) fails if T::T() is not public:

struct Base {protected: Base(){}};
struct Derived : private Base<int> {};

Is there any way to make this work for all cases? Are there any unmentioned issues with the code?

7
  • There used to be a trait std::bases and std::direct_bases part of the abandoned TR2, which is still shipped with GCC. You could use that to check if any of the bases are template instantiations. Commented Mar 23, 2014 at 15:14
  • 1
    This code is not correct because if U inherits from T<X> and T<Y> where X != Y, it does not detect the inheritance. Commented Mar 23, 2014 at 15:34
  • BTW: You may have compile time check with static_assert(isDerivedFrom<Base, Base_D1>::value, "unexpected value") instead of manual visual check with stream. Commented Mar 23, 2014 at 15:36
  • @Jarod42 Sure, thats why I wrote the code in the first place, printing to console just felt more natural for testing. Commented Mar 23, 2014 at 15:41
  • @JohannesSchaub The case of multiple inheritance did not occur to me, good catch. What solution do you suggest? Commented Mar 23, 2014 at 15:41

1 Answer 1

15

You may use: https://ideone.com/wR2dLX

template<template<class> class T, class U>
struct isDerivedFrom
{
private:
    template<class V>
    static decltype(static_cast<const T<V>&>(std::declval<U>()), std::true_type{})
    test(const T<V>&);

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

As private inheritance is not visible, the trait returns false in the last case (for struct Derived : private Base<int> {};).

Sign up to request clarification or add additional context in comments.

5 Comments

Could you please explain what decltype(static_cast<T<V>>(std::declval<U>()), std::true_type{}) does? Why is the static_cast required?
The static_cast is to try to (publicly) converts U to T<V> and use SFINAE to remove invalid case. (your code doesn't use SFINAE, just use the better match).
operator T<int> can fool this?
@Yakk no, because a conversion function is never invoked to convert to a base class type. And if T<V> is not a base-class type, template argument deduction would have failed before trying to do the conversion.
However, I belive that the static_cast can be fooled, if T<V> has a public constructor T(const U&). You may want to change the static_cast to static_cast<const T<V>&>, which should eliminate this loophole.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.