Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I check whether a member function has const overload?

Lets say I have

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

struct bar {
    void ham() {}
};

Assuming I have a templated function, can I tell whether given type has a const overload for ham?

like image 345
Xarn Avatar asked Dec 06 '22 19:12

Xarn


2 Answers

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

like image 146
Jarod42 Avatar answered Jan 25 '23 23:01

Jarod42


SFINAE over and over again. Here is another option which is unspecified about the return types but lets you specify the arguments.

(For comparison: the approach by @Jarod42 checks the exact signature, return type + arguments, the other void_t expression sfinae stuff up to now checks only whether ham() can be called.)

Plus, it works with the current MSVC 2015 Update 1 version (unlike the usual void_t stuff).

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 as

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

For the "newest" sfinae stuff, however, I suggest you have a look at boost.hana.

like image 44
davidhigh Avatar answered Jan 26 '23 00:01

davidhigh