如何从模板化函数的函数指针中提取参数类型并分支调用?
提取函数指针参数类型并实现分支逻辑的方案
完全可以实现提取函数指针的参数类型,并在模板中根据参数类型做编译期分支处理,以下是针对单参数和可变参数场景的具体实现:
一、单参数函数指针的实现
首先定义类型萃取模板,用来提取函数指针的返回值、参数类型等信息:
#include <type_traits> #include <iostream> // 基础模板,用于匹配函数指针 template<typename F> struct function_traits; // 特化:匹配单参数函数指针 template<typename Ret, typename Arg> struct function_traits<Ret(*)(Arg)> { using return_type = Ret; using first_arg_type = Arg; static constexpr size_t arg_count = 1; }; // 原模板函数,根据参数类型分支处理 template <typename FncPtr> void f(FncPtr fp) { using traits = function_traits<FncPtr>; if constexpr (std::is_same_v<typename traits::first_arg_type, double>) { fp(1.0); } else if constexpr (std::is_same_v<typename traits::first_arg_type, int>) { fp(25); } else { // 编译期断言,提示不支持的参数类型 static_assert(!std::is_same_v<FncPtr, FncPtr>, "Unsupported argument type for function pointer"); } } // 测试用例 void print_double(double d) { std::cout << "Double value: " << d << "\n"; } void print_int(int i) { std::cout << "Int value: " << i << "\n"; } int main() { f(print_double); // 输出:Double value: 1.0 f(print_int); // 输出:Int value: 25 // f(print_float); // 触发编译期错误:Unsupported argument type for function pointer return 0; }
二、可变参数函数指针的实现
扩展类型萃取模板,支持提取可变参数列表中的任意位置参数类型,同时处理无参数的情况:
#include <type_traits> #include <tuple> #include <iostream> // 基础模板 template<typename F> struct function_traits; // 特化:匹配可变参数函数指针 template<typename Ret, typename... Args> struct function_traits<Ret(*)(Args...)> { using return_type = Ret; // 提取第N个参数类型(N从0开始) template<size_t N> using arg_type = std::tuple_element_t<N, std::tuple<Args...>>; static constexpr size_t arg_count = sizeof...(Args); }; // 支持可变参数的模板函数 template <typename FncPtr> void f(FncPtr fp) { using traits = function_traits<FncPtr>; if constexpr (traits::arg_count == 0) { fp(); // 无参数时直接调用 } else if constexpr (traits::arg_count >= 1) { using first_arg = typename traits::template arg_type<0>; if constexpr (std::is_same_v<first_arg, double>) { fp(1.0, "extra string"); // 传入符合函数签名的参数 } else if constexpr (std::is_same_v<first_arg, int>) { fp(25, 3.14f, true); } else { static_assert(!std::is_same_v<FncPtr, FncPtr>, "Unsupported first argument type"); } } } // 测试用例 void var_func_double(double d, const char* s) { std::cout << "Double: " << d << ", Str: " << s << "\n"; } void var_func_int(int i, float f, bool b) { std::cout << "Int: " << i << ", Float: " << f << ", Bool: " << b << "\n"; } void no_arg_func() { std::cout << "No arguments called\n"; } int main() { f(var_func_double); // 输出:Double: 1, Str: extra string f(var_func_int); // 输出:Int: 25, Float: 3.14, Bool: 1 f(no_arg_func); // 输出:No arguments called return 0; }
注意事项
- 上述代码依赖C++17及以上标准,因为使用了
if constexpr实现编译期分支判断; - 类型萃取模板可以进一步扩展,支持
std::function、成员函数指针、lambda等其他可调用对象; - 编译期断言
static_assert用于在遇到不支持的参数类型时,直接在编译阶段报错,避免运行时问题。
内容的提问来源于stack exchange,提问作者SupAl
相关产品推荐
相关产品推荐

