Published by orzz.org(). (https://orzz.org/cxx-function-traits/)
在C++中,存在“可调用对象(callable objects)”这么一个概念。这里我直接摘录C++11标准《ISO/IEC 14882:2011》,§ 20.8.1 Definitions:
A callable type is a function object type (20.8) or a pointer to member.
A callable object is an object of a callable type.
同时,再根据 § 20.8 Function objects:
A function object type is an object type (3.9) that can be the type of the postfix-expression in a function call (5.2.2, 13.3.1.1). A function object is an object of a function object type. In the places where one would expect to pass a pointer to a function to an algorithmic template (Clause 25), the interface is specified to accept a function object. This not only makes algorithmic templates work with pointers to functions, but also enables them to work with arbitrary function objects.
我们可以总结出callable objects的基本定义:
一个callable objects用起来就像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
void func(void) { // ... } struct Foo { void operator()(void) { // ... } }; struct Bar { using fr_t = void(*)(void); static void func(void) { // ... } operator fr_t(void) { return func; } }; struct A { int a_; void mem_func(void) { // ... } }; int main(void) { void(* func_ptr)(void) = &func; // 1. 函数指针 func_ptr(); Foo foo; // 2. 仿函数 foo(); Bar bar; // 3. 可被转换为函数指针的类对象 bar(); void (A::*mem_func_ptr)(void) // 4. 类成员函数指针 = &A::mem_func; int A::*mem_obj_ptr // 或者是类成员指针 = &A::a_; A aa; (aa.*mem_func_ptr)(); aa.*mem_obj_ptr = 123; return 0; } |
从上面我们可以看到,除了类成员指针之外,上面的定义里涉及到的对象均可以像一个函数那样做调用操作。另外,由于函数类型并不能直接用来定义对象,因此上面对可调用类型的定义里并没有包含函数类型。
从实际使用的角度来说,如果我们需要萃取可调用体的各种属性,那么这个“可调用体”会和标准中的定义有些出入。我们至少需要包含以下类型:
首先,我们写一个namespace,把detail和外部的function_traits隔离开,这样就可以在function_traits真正执行之前做一次decay:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* Function type traits */ namespace function_traits_detail_ { template <typename F> struct impl_; } // namespace function_traits_detail_ template <typename F> struct function_traits : function_traits_detail_::impl_<typename std::decay<F>::type> {}; |
之后,通过impl的特化来处理各种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
// check functor template <typename F> struct impl_ : impl_<decltype(&F::operator())> {}; // check pointer template <typename T> struct impl_<T*> : impl_<T> {}; // check function pointer template <typename R, typename... P> struct impl_<R(*)(P...)> : impl_<R(P...)> {}; template <typename R, typename... P> struct impl_<R(*)(P..., ...)> : impl_<R(P..., ...)> {}; // check member function pointer #define FUNCTION_TRAITS_IMPL__(...) template <typename R, typename C, typename... P> struct impl_<R(C::*)(P...) __VA_ARGS__> : impl_<R(P...)> {}; template <typename R, typename C, typename... P> struct impl_<R(C::*)(P..., ...) __VA_ARGS__> : impl_<R(P..., ...)> {}; FUNCTION_TRAITS_IMPL__() FUNCTION_TRAITS_IMPL__(const) FUNCTION_TRAITS_IMPL__(volatile) FUNCTION_TRAITS_IMPL__(const volatile) #undef FUNCTION_TRAITS_IMPL__ // check std::function template <typename R, typename... P> struct impl_<std::function<R(P...)>> : impl_<R(P...)> {}; template <typename R, typename... P> struct impl_<std::function<R(P..., ...)>> : impl_<R(P..., ...)> {}; // check function type template <typename R, typename... P> struct impl_<R(P...)> { enum : std::size_t { arity = sizeof...(P) }; typedef R type(P...); typedef R return_type; typedef std::tuple<P...> parameters; }; template <typename R, typename... P> struct impl_<R(P..., ...)> { enum : std::size_t { arity = sizeof...(P) }; typedef R type(P..., ...); typedef R return_type; typedef std::tuple<P...> parameters; }; |
上面的实现里需要注意的几个地方如下:
Published by orzz.org(). (https://orzz.org/cxx-function-traits/)