简体   繁体   English

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

[英]Trait to check if some specialization of template class is base class of specific class

There is std::is_base_of in modern STL. 现代STL中有std::is_base_of It allow us to determine whether the second parameter is derived from first parameter or if they are the same classes both or, otherwise, to determine is there no such relation between them. 它允许我们确定第二个参数是从第一个参数派生还是它们是相同的类,或者确定它们之间是否存在这样的关系。

Is it possible to determine whether the one class is derived from some concrete template class without distinction of which concrete actual parameters involved to its specialization? 是否有可能确定一个类是否来自某个具体的模板类,而不区分其专业化所涉及的具体实际参数?

Say, we have; 说,我们有;

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

And

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

Is it possible to define a type trait: 是否可以定义类型特征:

template< typename T > is_derived_from_B;

Such that it is derived from std::true_type when T is any specialization of D and derived from std::false_type if T is not derived from any specialization of B ? 如果TD任何特化并且从std::false_type派生,如果T不是从B任何特化派生的,那么它是从std::true_type派生的?

If you can assume that a derived type uses a public inheritance from B<Args...> (and so the upcasting is possible), then you can use the following SFINAE: 如果您可以假设派生类型使用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;

Tests: 测试:

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 演示1

It can be generalized to accept any base class template : 它可以推广为接受任何基类模板

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;

Tests: 测试:

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 演示2

I want to propose another solution, that will also work in case of private inheritance. 我想提出另一个解决方案,这也适用于私有继承。 Downsides are that it requires you to modify the base class template and it is base class-specific. 缺点是它需要您修改基类模板,并且它是基类特定的。

Assuming your base class is template< typename... Args > class Base , you need to add a friend function to it: 假设你的基类是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
};

Then, you can write your trait: 然后,你可以写下你的特质:

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

This trait won't work in Visual Studio 2015 prior to Update 1, you'll have to write something like: 此特征在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;

The thing works because argument-name dependent lookup will find the friend function despite the private inheritance, and the friend function (or functions, if multiple are found) will then check is_base_of on an actual specialization. 事情是有效的,因为参数名称相关的查找将找到友元函数,尽管私有继承,并且友元函数(或函数,如果找到多个)将在实际的is_base_of上检查is_base_of

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM