特性检查模板类的某些特化是否是特定类的基类

时间:2014-09-15 10:07:30

标签: c++ c++11 traits c++14 typetraits

现代STL中有std::is_base_of。它允许我们确定第二个参数是从第一个参数派生还是它们是相同的类,或者确定它们之间是否存在这样的关系。

是否有可能确定一个类是否来自某个具体的模板类,而不区分其专业化所涉及的具体实际参数?

说,我们有;

template< typename ...types >
struct B {};

template< typename ...types >
struct D : B< types... > {};

是否可以定义类型特征:

template< typename T > is_derived_from_B;

如果std::true_typeT的任何专精,并且D来自std::false_type,如果T不是来自B的任何专业化,那么它来自{{1}} {{1}} {{1}}?

2 个答案:

答案 0 :(得分:11)

如果您可以假设派生类型使用B<Args...>的公共继承(因此可以进行向上转换),那么您可以使用以下SFINAE:

namespace detail
{
    template <typename Derived>
    struct is_derived_from_B
    {
        using U = typename std::remove_cv<
                                  typename std::remove_reference<Derived>::type
                                >::type;

        template <typename... Args>
        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*>()));
    };
}

template <typename Derived>
using is_derived_from_B = typename detail::is_derived_from_B<Derived>::type;

试验:

static_assert(is_derived_from_B<const D<int, char, float>>::value, "!");
static_assert(!is_derived_from_B<int>::value, "!");
static_assert(!is_derived_from_B<B<int,int>>::value, "!");
static_assert(!is_derived_from_B<std::vector<int>>::value, "!");

DEMO 1

可以推广接受任何基类模板

namespace detail
{
    template <template <typename...> class Base, typename Derived>
    struct is_derived_from_template
    {
        using U = typename std::remove_cv<
                                  typename std::remove_reference<Derived>::type
                                >::type;

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

        static std::false_type test(void*);

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

template <template <typename...> class Base, typename Derived>
using is_derived_from_template
                = typename detail::is_derived_from_template<Base, Derived>::type;

试验:

static_assert(is_derived_from_template<B, const D<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, int>::value, "!");
static_assert(!is_derived_from_template<B, B<int, int>>::value, "!");
static_assert(!is_derived_from_template<B, std::vector<int>>::value, "!");

DEMO 2

答案 1 :(得分:2)

我想提出另一个解决方案,这也适用于私有继承。缺点是它要求您修改基类模板,并且它是基类特定的。

假设你的基类是template< typename... Args > class Base,你需要添加一个友元函数:

template< /*whatever*/ > class Base {
  //...

  template< typename T >
  friend std::enable_if_t<
    std::is_base_of<Base, T>::value
  > is_derived_from_Base_impl(T const&); //unevaluated-only
};

然后,你可以写下你的特质:

template< typename T, typename Enable=void >
struct is_derived_from_Base : std::false_type { };

template< typename T >
struct is_derived_from_Base<T,
  decltype(is_derived_from_Base_impl(std::declval<T const&>()))
> : std::true_type { };

此特性在Update 1之前的Visual Studio 2015中无法运行,您必须编写如下内容:

namespace is_derived_from_Base_adl_barrier {
  struct no{}; //anything but void
  no is_derived_from_Base_impl(...);
  template< typename T >
  struct is_derived_from_Base : std::is_void<decltype(
    is_derived_from_Base_impl(std::declval<T const&>());
  )> { };
}
using is_derived_from_Base_adl_barrier::is_derived_from_Base;

事情是有效的,因为参数名称相关的查找将找到友元函数,尽管私有继承,并且友元函数(或函数,如果找到多个)将检查is_base_of实际的专业化。