我们来看看是否
struct Thing { int foo(double,bool) {return 0;} };
在编译期间有int foo(double,bool)成员函数.有很多方法可以做到这一点,大多数只是其他方式的变体.有人会想到一种与我在这里提到的5种方式截然不同(或至少相当有创意)的方式吗?我只是想学习一些模板和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,""); }
编辑:我刚刚想起了一个优雅而更通用的解决方案,Yakk很久以前就提出了一个不同的问题(这是他的实际打字,只是为了匹配foo函数而修改):
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?
一个相当有创意的方法可能是下面的那个.
它基于noexcept运算符和一个简单的使用声明(此处称为Type).
SFINAE和部分模板专业化完成剩下的工作.
它遵循一个最小的工作示例:
#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,"!"); }
如果与其他解决方案相比,该解决方案存在问题.
作为示例,下面的结构也将通过测试:
struct R { int f(double) {} };
换句话说,只要要测试的函数接受一个可以转换为42的类型的参数,并且无论返回类型是什么,它都被接受.