Other ways of checking if a class has a certain member function
Let's check if
struct Thing {
int foo(double, bool) {return 0;}
};
has the int foo(double, bool)
member function during compile time. There are many ways of doing this, and most are just variations of others. Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here? I'm just trying to learn some new techniques with templates and SFINAE.
#include <iostream>
#include <type_traits>
// Using void_t (this includes using std::is_detected).
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_foo : std::false_type {};
template <typename T>
struct has_foo<T,
void_t<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>
> : std::true_type {};
// Using the ... default argument.
template <typename T>
struct hasfoo {
template <typename U>
static std::true_type test (decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*); // or 'decltype(static_cast<int>(std::declval<U>().foo(double{}, bool{})))*' works fine too.
template <typename>
static std::false_type test (...);
static constexpr bool value = decltype(test<T>(nullptr))::value;
};
// Overloads and trailing return types.
template <typename>
struct Helper : std::true_type {};
template <typename T>
auto helper(int) -> Helper<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>;
template <typename>
std::false_type helper(long);
template <typename T>
constexpr bool hasFoo() {return decltype(helper<T>(0))::value;}
// Comma operator (basically the same as the above).
template <typename T>
auto check(int) -> decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})), std::true_type{});
template <typename T>
std::false_type check(...);
template <typename T>
using HasFoo = decltype(check<T>(0));
// Member function pointer template parameter.
template <typename T>
struct Hasfoo {
template <typename U, int(U::*)(double, bool)>
struct Tag;
template <typename U>
static constexpr bool test (Tag<U, &U::foo>*) {return true;}
template <typename>
static constexpr bool test (...) {return false;}
static constexpr bool value = test<T>(nullptr);
};
// Tests
struct Thing {
int foo(double, bool) {return 0;}
};
int main() {
static_assert (has_foo<Thing>::value, "");
static_assert (hasfoo<Thing>::value, "");
static_assert (hasFoo<Thing>(), "");
static_assert (HasFoo<Thing>::value, "");
}
Edit: I just remembered an elegant and more general solution that Yakk gave to a different question quite a while ago (here is his actual typing, modified only to match the foo function):
namespace meta {
namespace details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, decltype((void)(std::declval<Z<Ts...>>())), Ts...>:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
}
template<class T>
using member_foo = decltype(static_cast<int(T::*)(double, bool)>(&T::foo));
template<class T>
using has_member_foo = meta::can_apply<member_foo, T>;
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It is based on the noexcept
operator and a trivial using declaration (here named Type
).
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
#include<utility>
template<typename T, bool>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, noexcept(std::declval<T>().f(42))>>: std::true_type {};
struct S { void f(int) {} };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<T>::value, "!");
}
This solution has a problem if compared to the others.
As an example, the struct below would pass the test as well:
struct R { int f(double) {} };
In other terms, as long as the function to be tested accepts one argument to the type of which 42 can be cast and no matter what's the return type, it is accepted.
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It is based on the sizeof
operator and a trivial using declaration (here named Type
).
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
#include<cstddef>
template<typename T, std::size_t>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, sizeof(static_cast<void(T::*)(int)>(&T::f))>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?
A fairly creative way to do that could be the one below.
It doesn't exploit any unevaluated context. Instead, it relies on a trivial using declaration (here named Type
) and that's all.
SFINAE and partial template specialization do the rest.
It follows a minimal, working example:
#include<type_traits>
template<typename T, void(T::*)(int)>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, &T::f>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
链接地址: http://www.djcxy.com/p/91156.html
上一篇: Chrome扩展消息传递,当xhr调用在jQuery中失败时
下一篇: 其他检查类是否具有某个成员函数的方法