使用 SFINAE 检测模板化成员函数的存在
Use SFINAE to detect the existence of a templated member function
我了解到 SFINAE 可用于确定成员函数是否存在于 class 中。例如,以下代码可用于检查 hello
方法是否存在于 class.
中
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&) {...}
}
首先,向您展示原始代码的简化版本:
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;
}
我希望这适用于任何方法:模板化、重载等。
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
可以为您提供 return 类型的调用 .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。
我了解到 SFINAE 可用于确定成员函数是否存在于 class 中。例如,以下代码可用于检查 hello
方法是否存在于 class.
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&) {...}
}
首先,向您展示原始代码的简化版本:
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;
}
我希望这适用于任何方法:模板化、重载等。
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
可以为您提供 return 类型的调用 .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。