使用 SFINAE 检测模板化成员函数是否存在
Use SFINAE to detect the existence of a templated member function
我了解到SFINAE可以用来确定类中是否存在成员函数。例如,下面的代码可用于检查类中是否存在方法 hello
。
struct has_method_hello {
using yes = char[1];
using no = char[2];
template <typename U, typename C>
static constexpr yes& test(decltype(&U::hello));
template <typename>
static constexpr no& test(...);
static constexpr bool value = (sizeof(yes) == sizeof(test<T>(nullptr)));
};
struct Foo {
void hello() {}
}
std::cout << has_method_hello <Foo> :: value << std::endl; // 1
但是,假设hello
是模板化的,我该如何修改技巧以使其仍然可以正常运行?
struct Foo {
template <typename T>
void hello(T&) {...}
}
从这里:
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...>;
现在你想知道是否可以调用foo.hello(int&)
:
我们有hello_r
为您提供调用.hello
的返回类型:
template<class F, class...Ts>
using hello_r = decltype(std::declval<F>().hello( std::declval<Ts>()... ));
这导致can_hello
:
template<class F, class...Ts>
using can_hello = can_apply<hello_r, F, Ts...>;
现在
struct Foo {
template <typename T>
void hello(T&) {...}
};
int main() {
std::cout << can_hello<Foo&, int&>::value << 'n';
std::cout << can_hello<Foo&, char&>::value << 'n';
std::cout << can_hello<Foo&>::value << 'n';
}
打印 110。
活生生的例子。
首先,向您展示原始代码的缩短版本:
template <typename T>
struct has_method_hello {
static constexpr auto test(int) -> decltype(std::declval<T&>().hello(), std::true_type());
static constexpr std::false_type test(...);
using result_type = decltype(test(0));
static const bool value = result_type::value;
};
struct Foo {
void hello() {}
};
现在让它适用于模板参数,很简单,一个例子:
template <typename T>
struct has_method_hello {
static constexpr auto test(int) -> decltype(std::declval<T&>().hello(std::declval<int&>()), std::true_type());
static constexpr std::false_type test(...);
using result_type = decltype(test(0));
static const bool value = result_type::value;
};
struct Foo {
template <typename T>
void hello(T& v) {}
};
请注意,我在这里对int
类型进行了硬编码。您也可以将其作为模板的一部分has_method_hello
。
这可以做到:
// Example program
#include <iostream>
#include <string>
namespace mpl {
template<typename ...>
struct void_type
{
using type = void;
};
template<typename ...T>
using void_t = typename void_type<T...>::type;
} // namespace mpl
#define CAN_CALL_METHOD(NAME)
namespace internal {
template<typename T, typename ...Args>
using result_of_call_method_##NAME = decltype(
std::declval<T>().NAME(std::declval<Args>()...));
}
template<typename T, typename Signature, typename = void>
struct can_call_method_##NAME: std::false_type
{};
template<typename T, typename ...Args>
struct can_call_method_##NAME<T, void(Args...),
mpl::void_t<internal::result_of_call_method_##NAME<T, Args...>>
>: std::true_type
{};
template<typename T, typename R, typename ...Args>
struct can_call_method_##NAME<T, R(Args...),
typename std::enable_if<!std::is_void<R>::value &&
std::is_convertible<internal::result_of_call_method_##NAME<T, Args...>, R
>::value
>::type
>: std::true_type
{};
CAN_CALL_METHOD(hello);
struct Foo {
template <typename T>
void hello(T&) {}
};
struct Foo1 {
};
int main()
{
std::cout << std::boolalpha;
std::cout << can_call_method_hello<Foo, void(int&)>::value << std::endl;
std::cout << can_call_method_hello<Foo1, void(int&)>::value << std::endl;
}
IdeOne 链接
我希望这应该适用于任何方法:模板化、重载等。
相关文章:
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 将公共但非静态的成员函数与ALGLIB集成
- 使用指向成员的指针将成员函数作为参数传递
- 将重载的成员函数传递给函数模板
- 我不小心调用了一个没有自己类对象的成员函数.但这是怎么回事呢
- 如何在C++中使用非静态成员函数作为回调函数
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 关联容器的下界复杂性:成员函数与非成员函数
- 在 C++ 中用派生类型重写成员函数
- 链表的泛型函数remove()与成员函数remove)
- 如何将lambda作为模板类的成员函数参数
- constexpr构造函数需要常量成员函数时出现问题
- 将自由函数绑定为类成员函数
- 区分非成员函数和头文件中的成员函数
- 如何从子成员函数修改父公共成员变量
- 保留对其他类的成员函数的引用
- 在运算符重载定义中使用成员函数(const错误)
- 内联如何影响模块接口中的成员函数
- 将成员函数指针作为参数传递给模板方法