确定类型是否有潜在的重载方法
SFINAE to determine if a type has a potentially overloaded method
我正在寻找一个SFINAE解决方案来检查在编译时,如果一个类型有一个方法。我的目标是检查类型是否为有效的"duck类型",但我想使用static_assert
来提供信息消息,而不是无用的编译错误。
我找到了[这个问题],它为我的问题提供了一个相当好的答案,除了当类型为方法提供重载时失败:
template<typename...> // parameter pack here
using void_t = void;
template<typename T, typename = void>
struct has_xxx : std::false_type {};
template<typename T>
struct has_xxx<T, void_t<decltype(&T::xxx)>> :
std::is_member_function_pointer<decltype(&T::xxx)>{};
下面的例子可以很好地工作,并区分方法和成员变量:
struct Foo { int xxx() {return 0;}; };
struct Foo2 {};
struct Foo3{ static double xxx;};
double Foo3::xxx = 42;
int main() {
static_assert(has_xxx<Foo>::value, "");
static_assert(!has_xxx<Foo2>::value, "");
static_assert(!has_xxx<Foo3>::value, "");
}
原始现场演示
如果有重载,代码失败:
struct Foo { int xxx() {return 0;} void xxx(int){} };
int main() {
static_assert(has_xxx<Foo>::value, "");
}
使用重载方法的实时演示失败
如何改进这段代码来处理重载?
如果您正在做的是检查名称 xxx
是否存在于类型T
中,那么我们可以使用以下方法适用于所有非final
/union
类型T
。可以创建一个新类型,它公开继承T
和具有成员xxx
的回退类型。当且仅当T
没有开始的成员时,可以在派生类型上明确地访问xxx
。
template <class T>
class has_xxx_impl
{
private:
struct Fallback {
int xxx;
};
struct Derived : T, Fallback { };
template <class U>
static std::false_type test(decltype(&U::xxx)* );
template <class U>
static std::true_type test(...);
public:
using type = decltype(test<Derived>(nullptr));
};
template <class T>
struct has_xxx : has_xxx_impl<T>::type
{ };
如果你想检查更具体的东西——比如T
,你可以调用T{}.xxx()
,我会做不同的事情。
适当的duck类型检查是"您的xxx
是否可以用特定的签名调用,并且它的返回值在特定上下文中使用"。拥有一个重载的xxx
是没有用的,因为它关系到如何使用它。
我们从can_apply
namespace details {
template<template<class...>class Z, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:
std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
告诉你给定的参数是否可以合法地传递给某个模板。
然后表示我们想要的鸭子类型:
template<class T>
using xxx_result = decltype( std::declval<T>().xxx() );
template<class T>
using can_xxx = can_apply< xxx_result, T >;
和can_xxx<T>
是真还是假取决于我们是否可以做t.xxx()
。
如果需要类型限制,只需:
template<class T, class R>
using xxx_result_as_R = decltype( R(std::declval<T>().xxx()) );
template<class T, class R>
using can_xxx_as_R = can_apply< xxx_result_as_R, T, R >;
所以如果你想让xxx
返回int
-able的东西,我们得到:
template<class T>
using valid_xxx = can_xxx_as_R<T, int>;
相关文章:
- 重载方法的方式会在使用临时调用时生成编译器错误
- 使用模板参数重载C++方法:如何使其适用于模板的子类?
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 一种优雅或至少可行的方法,用于使用和接受具有重载方法和运算符的不同大小的文字数组常量
- 使用 nullptr 调用重载方法是不明确的
- 具有重载方法的可变参数数据结构
- C++11 重载方法,并转发到唯一方法
- 获取特定的模板重载方法指针
- std::result_of 应用于 const 重载方法
- 重载方法的类接受模板和基类 - 如何默认某个方法
- 推导模板类重载方法的地址会导致"error: expected primary-expression before ‘decltype’"
- C++如何使用按值传递和按引用传递的重载方法
- 具有原始方法参数派生类的 C++ 重载方法参数
- SWIG:在派生类中处理基类重载方法
- 在c++中的赋值运算符重载方法中删除旧的动态分配内存
- C++选择'wrong'默认参数的重载方法
- 重写重载方法会隐藏一些重载
- 在C++中,如何从父类变量的子类调用重载方法
- 所有重载方法的别名
- 使用 SWIG 在重载C++方法中设置类型