prestokeys
prestokeys

Reputation: 4849

Checking if a class has a function (return type and const checked)

Given

class A {
public:
    bool foo(int) const {return true;}
};

I want HasFooWithStringReturnTypeAndIsConst<A>::value and HasFooWithBoolReturnTypeAndIsNotConst<A>::value to be false (HasFooWithBoolReturnTypeAndIsConst<A>::value already returns true so that is working fine). Here is what I have:

#include <iostream>
#include <type_traits>
#include <string>

class A {
public:
    bool foo(int) const {return true;}
};

template <typename...> struct voider {using type = void;};

template <typename... Ts>
using void_t = typename voider<Ts...>::type;

template <typename T, typename = void_t<T>>
struct HasFooWithBoolReturnTypeAndIsNotConst : std::false_type {};

template <typename T>
struct HasFooWithBoolReturnTypeAndIsNotConst<T,
        void_t<decltype(std::declval<T&>().foo(std::declval<int>()))>> {
    using Foo = bool (T::*)(int);
    template <typename U> static std::true_type test (Foo*);
    template <typename U> static std::false_type test (...);
    static constexpr bool value = std::is_same<decltype(test<T>(nullptr)), std::true_type>::value;
};

template <typename T, typename = void_t<T>>
struct HasFooWithStringReturnTypeAndIsConst : std::false_type {};

template <typename T>
struct HasFooWithStringReturnTypeAndIsConst<T,
        void_t<decltype(std::declval<T&>().foo(std::declval<int>()))>> {
    using Foo = std::string (T::*)(int) const;
    template <typename U> static std::true_type test (Foo*);
    template <typename U> static std::false_type test (...);
    static constexpr bool value = std::is_same<decltype(test<T>(nullptr)), std::true_type>::value;
};

int main() {
    std::cout << HasFooWithStringReturnTypeAndIsConst<A>::value << '\n';  // true (should be false!)
    std::cout << HasFooWithBoolReturnTypeAndIsNotConst<A>::value << '\n';  // true (should be false!)
}

Can someone explain why they are returning true instead of false? How to fix them so that they return false? A::foo(int) is a const function that returns bool, so they are supposed to return false, aren't they?

Upvotes: 2

Views: 142

Answers (1)

Barry
Barry

Reputation: 303377

Your check is for:

decltype(test<T>(nullptr))

And the two overloads are:

template <typename U> static std::true_type test(Foo*);
template <typename U> static std::false_type test(...);

Nowhere in here are you actually considering &T::foo. You are just checking if you can convert nullptr to some arbitrary pointer type. Which, of course you can. That's why it ends up as true_type. What you want to check is if you can convert &T::foo specifically to that type:

template <typename U> static std::true_type test (std::string (U::*)(int) const );
template <typename U> static std::false_type test (...);
static constexpr bool value = decltype(test<T>(&T::foo))::value;

Note that you can do this all much simpler in the partial specialization directly via:

template <typename T>
struct HasFooWithStringReturnTypeAndIsConst<T,
            std::enable_if_t<
                std::is_same<std::string,
                             decltype(std::declval<const T&>().foo(0))
                             >::value
            >> : std::true_type { };

Here, we're checking if foo() is const by calling it on a const T&, and then simply checking that its return type is std::string.

Upvotes: 2

Related Questions