函数的类型主要集中在以下几种
后三者都是类对象,可以看成一种类型
template <typename T>
struct function_traits;
对于函数指针,存在两种情况
int pointer_func(int a, int b) {
return a + b;
}
// decltype(pointer_func) is int(int, int)
int pointer_func(int a, int b) {
return a + b;
}
template <typename Func>
void traits_test1(Func&&) {
}
template <typename Func>
void traits_test2(Func) {
}
int main() {
// Func = int(&)(int, int)
traits_test1(pointer_func);
// Func = int(*)(int, int)
traits_test2(pointer_func);
}
不难发现,函数指针的类型为
其中R是函数返回值类型,Args是函数的参数列表,针对这三种进行特化
template <typename R, typename... Args>
struct function_traits_helper
{
static constexpr auto param_count = sizeof...(Args);
using return_type = R;
template <std::size_t N>
using param_type = std::tuple_element_t<N, std::tuple<Args...>>;
};
// int(*)(int, int)
template <typename R, typename... Args>
struct function_traits<R(*)(Args...)> : public function_traits_helper<R, Args...>
{
};
// int(&)(int, int)
template <typename R, typename... Args>
struct function_traits<R(&)(Args...)> : public function_traits_helper<R, Args...>
{
};
// int(int, int)
template <typename R, typename... Args>
struct function_traits<R(Args...)> : public function_traits_helper<R, Args...>
{
};
假设在main函数中定义一个匿名函数lambda,通过模板参数类型推导判断它的类型
template <typename Func>
void traits_test(Func&&) {
}
int main()
{
auto f = [](int a, int b) { return a + b; };
// Func = main()::<lambda(int, int)> const
// Func::operator() = int(main()::<lambda(int, int)>*)(int, int) const
traits_test(f);
return 0;
}
lambda实际上是一个匿名函数对象,可以理解为内部也重载了operator()函数,所以如果将lambda整体进行推导,那么会推导出一个main()::
template <typename R, typename... Args>
struct function_traits_helper
{
static constexpr auto param_count = sizeof...(Args);
using return_type = R;
template <std::size_t N>
using param_type = std::tuple_element_t<N, std::tuple<Args...>>;
};
template <typename ClassType, typename R, typename... Args>
struct function_traits<R(ClassType::*)(Args...) const> : public function_traits_helper<R, Args...>
{
using class_type = ClassType;
};
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())> {};
通过std::function和std::bind绑定的函数对象和lambda类型相同,不需要额外的特化
针对某些可能推导出const的增加const版本,比如上述的lambda
完整代码请参考这里