I know that there are many possible ways to detect if a class has a specific function but non of them really work for my exact case. My current implementation to check for t
Here is one old school C++03 way of doing it. Typically it can be used as a utility and get it molded for any method or variable.
#define HasMember(NAME) \
template<class Class, typename Type = void> \
struct HasMember_##NAME \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}; \
template<class Class> \
struct HasMember_##NAME<Class, void> \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}
Instantiate:
HasMember(Foo);
Usage:
HasMember_Foo<B>::value // without type (but then no overload allowed)
HasMember_Foo<C, int (C::*)(float)>::value // needs type
Note that, here I am providing two HasMember_Foo
s, 1 with type and 1 without type. They are generalized for any type (not just specific to int (X::*)(float)
). If there is no type mentioned, then the class must have only 1 such method (without overload). Hence, it's always safer to mention the type; As you have done in your question, the specific type is int (X::*)(float)
. BTW, this also can be included using another macro.
Without such extra macro, in case of class C
and class D
, you may have to specify the type of the method.
Here is a demo with your code.
Here it's assumed that whichever class member (function or variable) is chosen, must be public
scoped. i.e. If X::foo
is private
then this solution will not work.
Here is a way to do it (work for your 4 test cases, did not test it intensively though), thanks @Jarod42 for the improvement (see initial answer at the end):
template <typename T>
int call_foo (int (T::*)(float));
template <typename C>
std::true_type has_foo(decltype(call_foo(&C::foo)));
template <typename C>
std::false_type has_foo (...);
template<typename T>
using HasFoo = decltype(has_foo<T>(0));
The problem with your code was that you were expecting U::*
whereas &B::foo
is A::*
(not B::*
). Here I let the compiler choose the value of T
by using implicit type deduction so I don't run into such issue.
The code works as follow:
T
does not have a foo
member, then the compiler will choose the second overload of has_foo
.T
does have a foo
member, the compiler will try the first overload but will fail since there is no matching call_foo
function so it will again choose the second one and make a std::false_type
.Working code on ideone: http://ideone.com/erh93I.
You can put everything in a class
if you want:
template <typename T>
class HasFoo {
template <typename C>
static int call_foo (int (C::*)(float));
template <typename C>
static std::true_type has_foo (decltype(call_foo(&C::foo)));
template <typename C>
static std::false_type has_foo (...);
public:
static constexpr bool value = decltype(has_foo<T>(0)){};
};
Here is a solution with <experimental/type_traits> or <boost/type_traits.hpp>
#include <experimental/type_traits>
#include <iostream>
struct A {
auto foo() { return 0; }
};
struct B {
auto bar() { return 0.0; }
};
struct C : public A {
auto bAr() { return 0.0; }
};
struct D : public C {
auto baR() { return 0.0; }
};
template <typename T>
using HasFoo_t = decltype(std::declval<T&>().foo());
int main() {
std::cout << std::experimental::is_detected_v<HasFoo_t, A> << std::endl;
std::cout << std::experimental::is_detected_v<HasFoo_t, B> << std::endl;
std::cout << std::experimental::is_detected_v<HasFoo_t, C> << std::endl;
std::cout << std::experimental::is_detected_v<HasFoo_t, D> << std::endl;
}