Is there a way to check the existence of a function (of a specific signature) regardless of whether it is templated or not? For example:
template <bool FLAG>
class A
{
public:
template <bool FLAG_ = FLAG>
std::enable_if_t<FLAG_, float> run(float a, float b)
{
return a + b;
}
};
class B
{
public:
float run(float a, float b)
{
return a + b;
}
};
Is it possible to write a generic trait, call it HasRun
, that would determine whether a class has the method run()
implemented? I.e.:
HasRun<A<true>>::value; // <---- would return true
HasRun<A<false>>::value; // <---- would return false
HasRun<B>::value; // <---- would return true
I'm aware of the following possible solution for non templated member functions:
template <typename T, typename = void>
struct HasRun : std::false_type
{
};
template <typename T>
struct HasRun<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::run)>::value>> : std::true_type
{
};
But this only works for B
. Similarly I could adapt it to
template <typename T>
struct HasRun<T, std::enable_if_t<std::is_member_function_pointer<decltype(&T::template run<true>)>::value>> : std::true_type
{
};
but this would work for A<true>
, would not work for B
, and would "erroneously" work for A<false>
(in the sense that A<false>::run()
isn't implemented).
As Bohdan Lakatosh has pointed out, the answer is quite straightforward using concepts. If you don't have access to concepts, but can use decltype
, here's another approach using decltype
, which will work for C++14 and up:
#include <type_traits>
#include <iostream>
template <bool FLAG>
class A {
public:
template <bool FLAG_ = FLAG>
std::enable_if_t<FLAG_, float> run(float a, float b) {
return a + b;
}
};
class B {
public:
float run(float a, float b) { return a + b; }
};
template <typename T, typename = void>
struct has_run : std::false_type {};
template <typename T>
struct has_run<T, std::enable_if_t<std::is_convertible<decltype(std::declval<T>().run(float{}, float{})), float>::value>> : std::true_type {};
template <typename T>
constexpr bool has_run_v = has_run<T>::value;
int main() {
std::cout << "has_run_v<B> = " << has_run_v<B> << '\n';
std::cout << "has_run_v<A<true>> = " << has_run_v<A<true>> << '\n';
std::cout << "has_run_v<A<false>> = " << has_run_v<A<false>> << '\n';
}