【问题标题】:Trait to check if some specialization of template class is base class of specific class检查模板类的某些特化是否是特定类的基类的特征
【发布时间】:2014-11-08 19:57:21
【问题描述】:

现代 STL 中有std::is_base_of。它可以让我们确定第二个参数是否派生自第一个参数,或者它们是否是相同的类,否则可以确定它们之间是否没有这种关系。

是否可以确定一个类是否派生自某个具体模板类而不区分其特化涉及哪些具体实际参数?

说,我们有;

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

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

是否可以定义类型特征:

template< typename T > is_derived_from_B;

这样当TD 的任何特化时它派生自std::true_type 并且如果T 不是从B 的任何特化派生自std::false_type

【问题讨论】:

  • @Nim 怎么用?它与标准的std::is_base_of 类型特征有足够的区别吗?
  • 对不起,重新格式化后才明白你的问题,忽略评论的提升部分..
  • 我认为像template&lt; typename ...types &gt; constexpr std::true_type helper(B&lt; types... &gt; const &amp;); template&lt; typename T &gt; constexpr std::false_type helper(T &amp;&amp;); decltype(helper(std::declval&lt; D const &amp; &gt;())) 这样的东西是可能的,如果允许这样的重载的话。
  • @Orient 否,T&amp;&amp; 是完全匹配的,因此与派生到基础的转换相比,重载解决方案总是首选。
  • @Orient 你可以用指针做到这一点:coliru.stacked-crooked.com/a/9d0d5ea3d60ec5e2 但它只适用于可访问的基类。

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


【解决方案1】:

如果您可以假设派生类型使用来自 B&lt;Args...&gt; 的公共继承(因此向上转换是可能的),那么您可以使用以下 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

【讨论】:

  • 这看起来很像 C++03 风格的特征。有什么特别的原因吗?
  • @dyp,C++11/14 风格的特征看起来如何?
  • 稍微短一点,也许更多 C++11 风格:coliru.stacked-crooked.com/a/04d62c4edab4bc84cc @Niall
  • @dyp 为什么不简单地using type = decltype(test( std::declval&lt;T*&gt;() )); 跳过value
  • My own version. 但我认为直接使用decltype 在编译时性能方面更有效。
【解决方案2】:

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

假设你的基类是template&lt; typename... Args &gt; 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 { };

此特性在更新 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 的实际特化。

【讨论】:

  • 过去 3 个小时我一直在寻找这个问题的解决方案。这太棒了,我羡慕那些有知识来建造这样东西的人。虽然我经常创建自定义类型特征,但我永远不会想出解决方案。你有我所有的尊重。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多