【问题标题】:std::is_base_of for template classesstd::is_base_of 用于模板类
【发布时间】:2016-04-12 20:44:17
【问题描述】:

A 是模板类时,有没有办法测试std::is_base_of<A, B>

template <typename X, typename Y> class A {};

template <typename X> class B : public A<X, char> {};

我想静态测试 std::is_base_of&lt;A, B&lt;int&gt;&gt; 之类的东西,B 派生自 A 的任何特化。 (为了更笼统,假设我们不知道 B 专门化 A 的方式,即 B 派生自 Achar>)

解决的一种方法是从(非模板)类派生 A,例如 C,然后检查 std::is_base_of&lt;C, B&lt;int&gt;&gt;。但是还有其他方法吗?

【问题讨论】:

  • 'A' 不是类型。你不能这样做,但你可以这样做 'std::is_base_of, B>'
  • 我之前回答过的类似问题。 stackoverflow.com/questions/34670375/…
  • my_is_base_of&lt;A, B&lt;int&gt;, char&gt; 对你来说是一个合理的用法吗?
  • @TartanLlama 谢谢。不过,这将非常具体到 B 专门化 A 的方式。如果可能的话,我正在寻找更通用的解决方案。
  • @Arunmu 谢谢。我明白了,这可以在我的设置中工作。

标签: c++ templates c++11


【解决方案1】:

您可以执行以下操作:

template <template <typename...> class C, typename...Ts>
std::true_type is_base_of_template_impl(const C<Ts...>*);

template <template <typename...> class C>
std::false_type is_base_of_template_impl(...);

template <typename T, template <typename...> class C>
using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));

Live Demo

但会因 multiple 继承或 privateA 继承而失败。

在 Visual Studio 2017 中,当基类模板有多个模板参数时,这将失败,并且无法推导出 Ts...

Demo

VS Bug Report

重构解决了 VS 的问题。

template < template <typename...> class base,typename derived>
struct is_base_of_template_impl
{
    template<typename... Ts>
    static constexpr std::true_type  test(const base<Ts...> *);
    static constexpr std::false_type test(...);
    using type = decltype(test(std::declval<derived*>()));
};

template < template <typename...> class base,typename derived>
using is_base_of_template = typename is_base_of_template_impl<base,derived>::type;

Live Demo

【讨论】:

  • 呵呵,我不知道Ts... 可以在这种情况下推导出来。不错!
  • 我得到了相同的解决方案,但是这个解决方案也失败(而不是返回false),来自A的单个非公共继承:(
  • @Jarod42:谢谢!这很聪明。
  • 看来你不需要is_base_of_template_impl中的typename T
  • @Sprite:不幸的是,我们没有语法来处理类型和非类型模板参数之间的通用语法。您必须创建类似的特征来处理类型为std::array
【解决方案2】:

在这里聚会有点晚了,但我想提供上述的变体

template < template <typename...> class Base,typename Derived>
struct is_base_of_template
{
    // A function which can only be called by something convertible to a Base<Ts...>*
    // We return a std::variant here as a way of "returning" a parameter pack
    template<typename... Ts> static constexpr std::variant<Ts...> is_callable( Base<Ts...>* );

    // Detector, will return type of calling is_callable, or it won't compile if that can't be done
    template <typename T> using is_callable_t = decltype( is_callable( std::declval<T*>() ) );

    // Is it possible to call is_callable which the Derived type
    static inline constexpr bool value = std::experimental::is_detected_v<is_callable_t,Derived>;

    // If it is possible to call is_callable with the Derived type what would it return, if not type is a void
    using type = std::experimental::detected_or_t<void,is_callable_t,Derived>;
};

template < template <typename...> class Base,typename Derived> 
using is_base_of_template_t = typename is_base_of_template<Base,Derived>::type;

template < template <typename...> class Base,typename Derived>
inline constexpr bool is_base_of_template_v = is_base_of_template<Base,Derived>::value;

这使用了建议的 is_detected 机制,我认为这使测试的意图更加清晰。但是,我现在可以同时获得用于实例化基类的类型,我认为这很有用。所以我可以写

template <typename T, typename U> struct Foo { };

struct Bar : Foo<int,std::string> { };

static_assert( is_base_of_template_v<Foo,Bar> );

// The variant type contains the types with which the Foo base is instantiated 
static_assert( std::is_same_v<std::variant<int,std::string>,is_base_of_template_t<Foo,Bar>> );

【讨论】:

  • 谢谢。这很整洁!
【解决方案3】:

以下解决方案适用于受保护的继承。

template <template <typename...> class BaseTemplate, typename Derived, typename TCheck = void>
struct test_base_template;

template <template <typename...> class BaseTemplate, typename Derived>
using is_base_template_of = typename test_base_template<BaseTemplate, Derived>::is_base;

//Derive - is a class. Let inherit from Derive, so it can cast to its protected parents
template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
{
    template<typename...T>
    static constexpr std::true_type test(BaseTemplate<T...> *);
    static constexpr std::false_type test(...);
    using is_base = decltype(test((test_base_template *) nullptr));
};

//Derive - is not a class, so it is always false_type
template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<!std::is_class_v<Derived>>>
{
    using is_base = std::false_type;
};

令人惊讶的是,在 VS2017 上,它适用于来自同一模板的多重继承,例如 C 和 C 两者。 (不知道怎么做!)

检查 Link to test code

【讨论】:

  • 您的解决方案非常好,但它有一个小错误尝试测试 static_assert(is_base_template_of<:variant std::variant>>::value);
【解决方案4】:

根据 Evgeny Mamontov 的回答,我认为正确的解决方案是

template <template <typename...> class BaseTemplate, typename Derived>
struct test_base_template<BaseTemplate, Derived, std::enable_if_t<std::is_class_v<Derived>>> : Derived
{
    template<typename...T>
    static constexpr std::true_type test(BaseTemplate<T...> *);
    static constexpr std::false_type test(...);
    using is_base = decltype(test((Derived *) nullptr));
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-24
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 1970-01-01
    相关资源
    最近更新 更多