I learned SFINAE can be used to determine whether a member function exists in a class or not. For example, the following code can be used to check if the method hello
is present in a 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
However, suppose the hello
is templated, how can I modify the trick so it can still function properly?
struct Foo {
template <typename T>
void hello(T&) {...}
}
From here:
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...>;
now you want to know if foo.hello(int&)
can be called:
We have hello_r
that gives you the return type of invoking .hello
:
template<class F, class...Ts>
using hello_r = decltype(std::declval<F>().hello( std::declval<Ts>()... ));
which leads to can_hello
:
template<class F, class...Ts>
using can_hello = can_apply<hello_r, F, Ts...>;
now
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';
}
prints 110.
live example.
First of all, showing you a shortened version of your original code:
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() {}
};
Now making it work for a template parameter, easy, an example:
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) {}
};
Note that, I have hardcoded int
type here. You can make that part of has_method_hello
template too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With