How to check if a member function has const overload?

Say I have

struct foo { void ham() {} void ham() const {} }; struct bar { void ham() {} }; 

Assuming I have a boilerplate function, can I tell if a given type of const has an overload for ham ?

+3
source share
5 answers

SFINAE time and time again. Here is another parameter that is not specified in the return data types, but allows you to specify arguments.

(For comparison: the approach by @ Jarod42 verifies the exact signature, returns type + arguments, another void_t expression sfinae stuff expression still checks if ham() can be called.)

In addition, it works with the current version of MSVC 2015 Update 1 (unlike the usual void_t material).

 template<typename V, typename ... Args> struct is_callable_impl { template<typename C> static constexpr auto test(int) -> decltype(std::declval<C>().ham(std::declval<Args>() ...), bool{}) { return true; } template<typename> static constexpr auto test(...) { return false; } static constexpr bool value = test<V>(int{}); using type = std::integral_constant<bool, value>; }; template<typename ... Args> using is_callable = typename is_callable_impl<Args...>::type; 

Use it like

 struct foo { void ham() {} void ham() const {} int ham(int) const {} }; int main() { std::cout <<is_callable<foo>::value //true <<is_callable<const foo>::value //true <<is_callable<const foo, int>::value //true <<is_callable<const foo, double>::value //also true, double is converted to int <<is_callable<const foo, std::string>::value //false, can't call foo::ham(std::string) const <<std::endl; } 

Demo on coliru

However, for the β€œlatest” sfinae materials, I suggest you take a look at boost.hana .
+3
source

WITH

 #define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \ template <typename U> \ class traitsName \ { \ private: \ template<typename T, T> struct helper; \ template<typename T> \ static std::uint8_t check(helper<signature, &funcName>*); \ template<typename T> static std::uint16_t check(...); \ public: \ static \ constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \ } DEFINE_HAS_SIGNATURE(has_ham_const, T::ham, void (T::*)() const); 

And then

 static_assert(has_ham_const<foo>::value, "unexpected"); static_assert(!has_ham_const<bar>::value, "unexpected"); 

Demo

+6
source

Detector (e.g. is_detected ):

 template <typename...> using void_t = void; template <typename T, template <typename> class D, typename = void> struct detect : std::false_type {}; template <typename T, template <typename> class D> struct detect<T, D, void_t<D<T>>> : std::true_type {}; 

Member Verification Example:

 template <typename T> using const_ham = decltype(std::declval<const T&>().ham()); 

Test:

 static_assert(detect<foo, const_ham>::value, "!"); static_assert(!detect<bar, const_ham>::value, "!"); 

Demo

+3
source

Another option is the void_t simulation (for official appearance in C ++ 17), which uses the SFINAE expression to make sure your function has the ability to be called in a const instance, regardless of the type of value returned.

 #include <iostream> #include <type_traits> struct Foo { void ham() const; void ham(); }; struct Bar { void ham() {} }; template<typename...> using void_t = void; template<typename C, typename = void> struct has_const_ham: std::false_type{}; template<typename C> // specialization, instantiated when there is ham() const struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : std::true_type{}; int main() { std::cout << std::boolalpha; std::cout << has_const_ham<Foo>::value << std::endl; std::cout << has_const_ham<Bar>::value << std::endl; } 

EDIT

If you want to force the return type, output the specialization from std::is_same , for example

 template<typename C> // specialization, instantiated when there is ham() const struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : std::is_same<decltype(std::declval<const C&>().ham()), void> // return must be void {}; 

Live on coliru

+2
source

Here is a macro-free solution that also doesn't care about return types:

 template <typename T> struct is_well_formed : std::true_type { }; template <typename T, typename = void> struct has_const_ham : std::false_type { }; template <typename T> struct has_const_ham<T, typename std::enable_if<is_well_formed<decltype( std::declval<const T&>().ham())>::value>::type> : std::true_type { }; static_assert(has_const_ham<foo>::value, "oops foo"); static_assert(!has_const_ham<bar>::value, "oops bar"); 
+1
source

All Articles