使用 SFINAE 在 C++ 中检查模板 parent class
Checking for template parent class in C++ using SFINAE
我最近一直在用 C++ 学习 SFINAE 的概念,目前我正在尝试在一个项目中使用它。
问题是,我正在尝试做的事情与我能找到的任何事情都不一样,而且我不知道该怎么做。
假设我有一个名为 MyParent 的模板class:
template <typename Elem>
class MyParent;
还有一个名为 MyClass 的 non-template class 继承了它,使用 char 作为 Elem:
class MyClass : public MyParent<char>;
现在,我想使用 SFINAE 来检查类型名称是否继承 MyParent
,而不管使用什么 Elem
类型。
由于 parent 的模板,我无法使用 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<MyParent<Elem>>::value
,它会给我 true
。哪个好。
但是,当我检查 is_my_parent<MyClass>::value
时,我收到 false
。哪一种说得通,因为 MyClass
实际上不是 MyParent<Elem>
,但我没能得到我想要的。
除了为继承自 MyParent
的每个 class 定义 is_my_parent
之外,在 C++ 中是否有任何方便的方法来实现这样的事情?
你可能会
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*>()));
Is there any convenient way to achive such a thing in C++, other than defining is_my_parent for each and every class that inherites from MyParent?
有,但您需要使用更精细的元编程技术。可以说完全回归基础。
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))) };
};
它依赖于函数重载和模板的两个基本属性:
- 派生的 class 可用于匹配以基本 class 模板作为参数的函数模板。
- Ellipsis 提供了一个转换序列,该序列总是被认为比其他任何序列都差。
然后只需检查所选重载的 return 类型以确定我们得到了什么。除了类型别名,您甚至可以在 C++03 中使用它。或者您可以对其进行现代化改造,只要重载解析为您完成工作,检查就会以同样的方式执行。
我更喜欢 Jarod42 的回答,但实际的 SNINAE 方法有点接近您的尝试是可行的。这是我想出的。
要使用 type_traits 来回答这个问题,我们需要知道元素的类型。我们可以让 MyParent
公开它:
template <typename Elem>
class MyParent {
public:
using ElemType = Elem;
};
然后默认(假)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 年精彩的两部分演讲中向全世界曝光。推荐,即使只是为了审查。
https://www.youtube.com/watch?v=Am2is2QCvxY
我最近一直在用 C++ 学习 SFINAE 的概念,目前我正在尝试在一个项目中使用它。
问题是,我正在尝试做的事情与我能找到的任何事情都不一样,而且我不知道该怎么做。
假设我有一个名为 MyParent 的模板class:
template <typename Elem>
class MyParent;
还有一个名为 MyClass 的 non-template class 继承了它,使用 char 作为 Elem:
class MyClass : public MyParent<char>;
现在,我想使用 SFINAE 来检查类型名称是否继承 MyParent
,而不管使用什么 Elem
类型。
由于 parent 的模板,我无法使用 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<MyParent<Elem>>::value
,它会给我 true
。哪个好。
但是,当我检查 is_my_parent<MyClass>::value
时,我收到 false
。哪一种说得通,因为 MyClass
实际上不是 MyParent<Elem>
,但我没能得到我想要的。
除了为继承自 MyParent
的每个 class 定义 is_my_parent
之外,在 C++ 中是否有任何方便的方法来实现这样的事情?
你可能会
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*>()));
Is there any convenient way to achive such a thing in C++, other than defining is_my_parent for each and every class that inherites from MyParent?
有,但您需要使用更精细的元编程技术。可以说完全回归基础。
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))) };
};
它依赖于函数重载和模板的两个基本属性:
- 派生的 class 可用于匹配以基本 class 模板作为参数的函数模板。
- Ellipsis 提供了一个转换序列,该序列总是被认为比其他任何序列都差。
然后只需检查所选重载的 return 类型以确定我们得到了什么。除了类型别名,您甚至可以在 C++03 中使用它。或者您可以对其进行现代化改造,只要重载解析为您完成工作,检查就会以同样的方式执行。
我更喜欢 Jarod42 的回答,但实际的 SNINAE 方法有点接近您的尝试是可行的。这是我想出的。
要使用 type_traits 来回答这个问题,我们需要知道元素的类型。我们可以让 MyParent
公开它:
template <typename Elem>
class MyParent {
public:
using ElemType = Elem;
};
然后默认(假)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 年精彩的两部分演讲中向全世界曝光。推荐,即使只是为了审查。 https://www.youtube.com/watch?v=Am2is2QCvxY