如何实现可包装任意可调用对象(含类成员函数)的C++模板
解决C++模板函数包装任意可调用对象(含成员函数)的问题
你的核心问题在于:非静态成员函数指针的类型和普通函数指针完全不同——前者必须绑定类的实例才能调用,而你最初的模板只支持普通函数/静态成员函数的指针类型。下面是几种无需大量样板代码的解决方案:
方案1:用std::invoke实现通用包装器(C++17及以上)
这是最简洁通用的方案,利用C++17引入的std::invoke可以统一处理所有可调用对象(普通函数、静态成员、非静态成员、lambda、函数对象等)。
修改你的包装器模板:
#include <iostream> #include <functional> // 引入std::invoke template <typename Callable, typename... ArgsT> auto wrapper(Callable&& func, ArgsT&&... args) { // 此处可添加你的自定义包装逻辑,比如日志、性能统计等 return std::invoke(std::forward<Callable>(func), std::forward<ArgsT>(args)...); }
调用时,非静态成员函数需要传入类实例(this或者具体对象)作为第一个参数:
void wrap_funcs() { std::cout << "Wrapped member = " << wrapper(&TestClass::mult_by_2_member, this, 2) << std::endl; std::cout << "Wrapped static = " << wrapper(&TestClass::mult_by_2_static, 2) << std::endl; }
std::invoke会自动识别成员函数指针,帮你完成(obj->*func)(args)的调用逻辑,无需手动处理类型差异。
方案2:重载模板适配成员函数指针(C++11及以上)
如果需要兼容C++17之前的标准,可以为非静态成员函数指针专门重载包装器:
#include <iostream> // 适配普通函数、静态成员函数的版本 template <typename T, typename... ArgsT> T wrapper(T(*func)(ArgsT...), ArgsT... args) { return func(args...); } // 适配非静态成员函数的版本 template <typename Ret, typename Class, typename... ArgsT> Ret wrapper(Ret(Class::*func)(ArgsT...), Class* obj, ArgsT... args) { return (obj->*func)(args...); } // 可选:适配const非静态成员函数 template <typename Ret, typename Class, typename... ArgsT> Ret wrapper(Ret(Class::*func)(ArgsT...) const, const Class* obj, ArgsT... args) { return (obj->*func)(args...); }
调用方式和方案1完全一致,编译器会根据传入的指针类型自动匹配对应的重载版本。
方案3:用lambda临时包装成员函数
如果不想修改原有的包装器模板,也可以在调用时用lambda把成员函数转换成普通可调用对象:
void wrap_funcs() { std::cout << "Wrapped member = " << wrapper([this](int a){ return this->mult_by_2_member(a); }, 2) << std::endl; std::cout << "Wrapped static = " << wrapper(&TestClass::mult_by_2_static, 2) << std::endl; }
这种方式适合临时适配少量场景,但大量使用会产生冗余代码,不如前两种方案优雅。
内容的提问来源于stack exchange,提问作者deponovo
相关产品推荐
相关产品推荐

