【问题标题】:Checking for template parent class in C++ using SFINAE使用 SFINAE 在 C++ 中检查模板父类
【发布时间】:2023-03-25 18:45:01
【问题描述】:

我最近一直在学习 C++ 中 SFINAE 的概念,目前正在尝试在项目中使用它。

问题是,我正在尝试做的事情与我能找到的任何事情都不一样,而且我不知道该怎么做。

假设我有一个名为 MyParent 的模板类:

template <typename Elem>
class MyParent;

还有一个名为 MyClass 的非模板类,它继承了它,使用 char 作为 Elem:

class MyClass : public MyParent<char>;

现在,我想使用 SFINAE 来检查类型名是否继承 MyParent,无论使用什么 Elem 类型。

我不能使用std::is_base_of,因为父母的模板。

我已尝试执行以下操作:

template <typename T>
struct is_my_parent : std::false_type {};
template <typename Elem>
struct is_my_parent<MyParent<Elem>> : std::true_type {};

现在,如果我检查is_my_parent&lt;MyParent&lt;Elem&gt;&gt;::value,它会给我true。哪个好。 但是,当我检查 is_my_parent&lt;MyClass&gt;::value 时,我收到了 false。哪一种有意义,因为MyClass 实际上不是MyParent&lt;Elem&gt;,但我没能得到我想要的。

除了为继承自MyParent 的每个类定义is_my_parent 之外,还有什么方便的方法可以在C++ 中实现这样的目标吗?

【问题讨论】:

  • 你可以添加到每个类,using parent_t = MyParent&lt;template_type&gt;;,然后你可以检查。尝试编写一些代码来检查此类是否继承自任何 MyParent&lt;T&gt; 基本上是不可能的,因为 T 可能是无限的类型集。
  • 你可以让MyParent模板继承自一个空的class MyParentCommonBase{};

标签: c++ templates inheritance sfinae


【解决方案1】:

你可能会这样做

template <typename T>
std::true_type is_my_parent_impl(const MyParent<T>*);

std::false_type is_my_parent_impl(const void*);

template <typename T>
using is_my_parent = decltype(is_my_parent_impl(std::declval<T*>()));

Demo

【讨论】:

  • 我喜欢这个解决方案,非常简单直接。谢谢!
【解决方案2】:

除了为从 MyParent 继承的每个类定义 is_my_parent 之外,还有什么方便的方法可以在 C++ 中实现这样的目标吗?

有,但您需要使用更精细的元编程技术。完全回到原来的样子。

template <class C>
class is_my_parent {
    using yes = char;
    using no  = char[2];
    
    template<typename t>
    static yes& check(MyParent<t> const*);

    static no& check(...);

public:
    enum { value = (1 == sizeof check(static_cast<C*>(0))) };
};

它依赖于函数重载和模板的两个基本属性:

  1. 派生类可用于匹配以基类模板作为参数的函数模板。
  2. Ellipsis 提供的转换序列总是被认为比任何其他序列都差。

然后只需检查所选重载的返回类型以确定我们得到了什么。除了类型别名,你甚至可以在 C++03 中使用它。或者您可以对其进行现代化改造,只要重载解决方案为您完成工作,检查将同样执行。

【讨论】:

  • 我其实很熟悉这种类型的实现,除了我通常更喜欢使用 uint8_t 和 uint16_t 而不是 char 和 char[2]。如果我可以将两个答案标记为有帮助,我也会标记这个答案。谢谢!
【解决方案3】:

我更喜欢 Jarod42 的回答,但实际的 SNINAE 方法与您的尝试有些接近。这是我想出的。

要使用 type_traits 来回答这个问题,我们需要知道元素的类型。我们可以让MyParent暴露它:

template <typename Elem>
class MyParent {
public:
    using ElemType = Elem;
};

那么默认 (false) is_my_parent 需要一个额外的 arg 并且可以使用 void_t 技术*:

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

template <typename T>
struct is_my_parent<T, std::void_t<typename T::ElemType>> : 
    std::is_base_of<MyParent<typename T::ElemType>, T>::type {};

仅当 ElemType 是 T 中的可访问类型时,特化才有效,如果继承关系成立,则结果为 std::true|false 类型。

实时示例:https://godbolt.org/z/na5637Knd

但是,函数重载解析不仅是一种更好的简化和大小的方法,而且编译速度也会更快。

(*) void_t 在 Walter Brown 2014 年精彩的 2 部分演讲中向世人展示。推荐即使只是为了审查。 https://www.youtube.com/watch?v=Am2is2QCvxY

【讨论】:

    猜你喜欢
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多