C++17中如何定义检测自定义仿函数的类型特性is_functor?
Implementing
is_functor Type Trait in C++17 Alright, let's break down how to build the is_functor type trait you need. First, let's recap the requirements clearly:
- A "functor" here is a class or union (can be marked
final) - It must have at least one publicly overloaded
operator()(this includes template operators, const/volatile-qualified operators, etc.)
Step 1: Include Required Headers
First, we'll need standard library headers for type traits and value manipulation:
#include <type_traits> #include <utility> // For std::declval
Step 2: Helper Trait to Detect operator()
We need a way to check if a type has a public operator(), covering both non-template and template versions of the operator. We'll use SFINAE (Substitution Failure Is Not An Error) to handle this:
namespace detail { // Helper trait to detect any public operator() template <typename T> struct has_call_operator { private: // First check: detect non-template operator() by taking its address template <typename U> static constexpr auto check(int) -> decltype(&U::operator(), bool{}) { return true; } // Second check: detect if we can call the operator with an int argument (covers template operators) template <typename U> static constexpr auto check(long) -> decltype(std::declval<U>()(std::declval<int>()), bool{}) { return true; } // Third check: detect if we can call the operator with no arguments template <typename U> static constexpr auto check(char) -> decltype(std::declval<U>()(), bool{}) { return true; } // Fallback: no operator() found template <typename U> static constexpr bool check(...) { return false; } public: static constexpr bool value = check<T>(0); // Prioritize checks from most to least specific }; }
This helper works by trying increasingly general checks:
- First, it tries to get the address of a non-template
operator()(most specific case) - If that fails, it tries calling the operator with an
int(covers template operators that accept integers, which is a common case) - If that fails, it tries a no-argument call
- If all checks fail, it returns
false
Step 3: Implement the is_functor Trait
Now we combine the class/union check with our has_call_operator helper:
template <class T> struct is_functor { static constexpr bool value = (std::is_class_v<T> || std::is_union_v<T>) && detail::has_call_operator<T>::value; }; template <class T> inline constexpr bool is_functor_v = is_functor<T>::value;
Step 4: Test Cases
Let's verify this trait with some common scenarios:
// Valid functors struct SimpleFunctor { void operator()() {} }; final struct FinalFunctor { void operator()(int) const noexcept {} }; union UnionFunctor { void operator()(double) {} }; struct TemplateFunctor { template <typename U> void operator()(U) {} }; auto lambda = []() {}; // Lambda closure is a class with public operator() // Invalid cases struct PrivateOp { private: void operator()() {} }; using FuncPtr = void(*)(); int basic_type = 42; static_assert(is_functor_v<SimpleFunctor>); static_assert(is_functor_v<FinalFunctor>); static_assert(is_functor_v<UnionFunctor>); static_assert(is_functor_v<TemplateFunctor>); static_assert(is_functor_v<decltype(lambda)>); static_assert(!is_functor_v<PrivateOp>); static_assert(!is_functor_v<FuncPtr>); static_assert(!is_functor_v<decltype(basic_type)>);
All these assertions should pass in a C++17-compliant compiler.
Key Notes
- The trait correctly ignores non-class/non-union types (like function pointers or basic types)
- It handles
finalclasses and unions properly - Template
operator()are detected via the call-based checks - Private
operator()are rejected (since the SFINAE checks can't access them)
内容的提问来源于stack exchange,提问作者Vincent
相关产品推荐
相关产品推荐

