Reputation: 4849
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
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