使用SFINAE分辨率区分普通模板参数和模板模板参数的技术

时间:2014-09-17 03:10:50

标签: c++ templates c++11 sfinae typetraits

最近受到启发的问题出现了question about extended std::is_base_of type trait

是否有任何技术可以让我们区分现代 C ++中的普通模板参数和模板模板参数或其扩展名(例如-std=gnu++1z clang ++ /的克++ )?

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 * >()));

在积极的情况下,它允许我们使一些有用的类型特征更强大(例如,STL的std::is_base_of)。

我认为它需要语言功能作为“通用类型名称”,不是吗?

2 个答案:

答案 0 :(得分:3)

类模板只能有一组模板参数,但您可以使用重载constexpr函数模板来调度相应的类模板。使用额外的SFINAE参数获取linked question中的is_derived_from特征,这样当B无法访问或模糊不清时,您就不会遇到严重错误:

#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

答案 1 :(得分:1)

你问:

  

是否有任何技术可以让我们区分现代C ++中的普通模板参数和模板模板参数或其扩展名(例如-std=gnu++1z clang ++ / g ++)?

在我看来,你需要像:

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
{
};

示例程序

#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;
}

输出:

true
false