find typename of typename in template parameter

356 views Asked by At

I would like the following code to compile when foo gets anything derived from base, otherwise a compile error ensues. I have written the type-trait class is_Base because the std::is_base_of does not work well with my template stuff. I am close. I got it to work using a static_passoff thing, but I would like to not have to use it. So how can write the enable_if without the static_passoff hack? Here is the running version: http://coliru.stacked-crooked.com/a/6de5171b6d3e12ff

#include <iostream>
#include <memory>

using namespace std;

template < typename D >
class Base
{
public:
    typedef D EType;
};

template<class T>
struct is_Base
{
    using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

    template<class U>
    static constexpr std::true_type test(Base<U> *) { return std::true_type(); }
    static constexpr std::false_type test(...) { return std::false_type(); }

    using value = decltype( test((T*)0) );
};

template < typename A >
using static_passoff = std::integral_constant< bool, A::value >;

template <typename T, typename = typename std::enable_if< static_passoff< typename is_Base< T >::value >::value >::type >
void foo(T const&)
{
}


class Derived : public Base<Derived> {};
class NotDerived {};


int main()
{
    Derived d;
    //NotDerived nd;

    foo(d);
    //foo(nd); // <-- Should cause compile error

    return 0;
}
2

There are 2 answers

7
Barry On BEST ANSWER

I'm not entirely sure I understand your question given that your code does work. But stylistically, for metafunctions that yield a type, that type should be named type. So you should have:

using type = decltype( test((T*)0) );
      ^^^^

Or, to avoid the zero-pointer-cast-hack:

using type = decltype(test(std::declval<T*>()));

Also, your test doesn't need a definition. Just the declaration. We're not actually calling it, just checking its return type. It doesn't have to be constexpr either, so this suffices:

template<class U>
static std::true_type test(Base<U> *);
static std::false_type test(...);

Once you have that, you can alias it:

template <typename T>
using is_Base_t = typename is_Base<T>::type;

And use the alias:

template <typename T, 
          typename = std::enable_if_t< is_Base_t<T>::value>>
void foo(T const&)
{
}
2
Cory On

After stumbling blindly into the answer in the comments, I found out I can just use is_Base<T>::type::value without any typename keywords. When trying to remove the static_passoff before, I kept putting in typename. I have always been mixed up with that one. Anyway, here is the final code with a few teaks from Barry's answer:

#include <iostream>
#include <memory>

using namespace std;

template < typename D >
class Base
{
public:
    typedef D EType;
};

template<class T>
struct is_Base
{
    using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

    template<class U>
    static constexpr std::true_type test(Base<U> *) { return std::true_type(); }
    static constexpr std::false_type test(...) { return std::false_type(); }

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

template <typename T, typename = typename std::enable_if< is_Base< T >::type::value >::type >
void foo(T const&)
{
}


class Derived : public Base<Derived> {};
class NotDerived {};


int main()
{
    Derived d;
    //NotDerived nd;

    foo(d);
    //foo(nd); // <-- Should cause compile error

    return 0;
}