Technique for distinguishing between normal template parameters and template template parameters using SFINAE permission

Question raised by a recently raised question about extended type traitstd::is_base_of

.

Is there any method that allows us to distinguish between a regular templated parameter and a templated template in modern C ++ or its extensions (say -std=gnu++1z

clang ++ / g ++ )?

namespace details
{

template< /* ??? */ base >
struct is_derived_from;

template< typaneme base >
struct is_derived_from< base >
{
    static std::true_type test(base *);
    static std::false_type test(void *);
};

template< template< typename ...formal > base >
struct is_derived_from< /* ??? */ >
{
    template< typename ...actual > // actual parameters must be here!
    static std::true_type test(base< actual... > *);
    static std::false_type test(void *);
};

} // namespace details

template< typename derived, /* ??? */ base >
using is_derived_from = decltype(details::is_derived_from< /* ? base< ? > */ >::test(std::declval< typename std::remove_cv< derived >::type * >()));

      

If so, this allows us to make some useful trait types more powerful (like STL std::is_base_of

).

I think this requires a language feature like "generic typenames", right?

+3


source to share


2 answers


There can only be one set of template parameters for class templates, but you can use constexpr

function template overloading instead of sending the appropriate class template. Take the trait is_derived_from

in the linked question with the optional SFINAE parameter to avoid getting a hard error when the B

base is unavailable or ambiguous

#include <type_traits>
namespace detail
{
    template <template <class...> class B, typename Derived>
    struct is_derived_from
    {
        using U = typename std::remove_cv<Derived>::type;

        template <typename... Args, 
                  typename = std::enable_if_t<
                             std::is_convertible<U*, Base<Args...>*>::value>>
        static auto test(B<Args...>*)
            -> typename std::integral_constant<bool
                                           , !std::is_same<U, B<Args...>>::value>;

        static std::false_type test(void*);

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

    using std::is_base_of; // may want to use is_convertible instead to match
                           // the semantics of is_derived_from
}

template <template <class...> class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; }

template <class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; }

struct B {};
struct D : B {};

template<class ...>
struct B2 {}; 
struct D2 : B2<int, double> { };

int main() {
  static_assert(my_is_base_of<B2, D2>(), "Oops");
  static_assert(my_is_base_of<B, D>(), "Oops");
  static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops");
  static_assert(!my_is_base_of<B, D2>(), "Oops");
}

      



Demo .

+3


source


You asked:

Is there any method that allows us to distinguish between a regular template parameter and a templated template in modern C ++ or its extensions (say -std=gnu++1z

clang ++ / g ++)?

It seems to me that you want something like:

template <typename T>
struct is_template_template : public std::false_type
{
};

template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};

      



Sample program

#include <iostream>

template <typename T>
struct is_template_template : public std::false_type
{
};

template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};

template <typename T> struct A {};
struct B {};

int main()
{
   std::cout << std::boolalpha;
   std::cout << is_template_template<A<int>>::value << std::endl;
   std::cout << is_template_template<B>::value << std::endl;
   return 0;
}

      

Output:

true
false
+1


source







All Articles