我们来看看是否
- 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的类型的参数,并且无论返回类型是什么,它都被接受.