如何在编译时自动判断成员函数的参数类型是否为void
解决方案:自动推导成员函数参数类型的包装器实现
核心思路是通过模板元编程提取成员函数的参数信息,编译期自动判断目标函数是无参还是单参,从而省去手动传递ArgType的步骤,同时避免分支编译冲突。
完整实现代码
#include <cstring> #include <utility> #include <tuple> #include <type_traits> using namespace std; // 成员函数特性提取器:解析成员函数指针的参数个数与类型 template<typename Func> struct MemberFuncTraits; // 特化:无参成员函数 template<typename Ret, typename Class> struct MemberFuncTraits<Ret(Class::*)()> { static constexpr size_t ArgCount = 0; using ReturnType = Ret; }; // 特化:单参成员函数 template<typename Ret, typename Class, typename Arg> struct MemberFuncTraits<Ret(Class::*)(Arg)> { static constexpr size_t ArgCount = 1; using ReturnType = Ret; using ArgType = Arg; }; // 辅助模板:简化参数个数的获取 template<typename Func> constexpr size_t MemberFuncArgCount = MemberFuncTraits<Func>::ArgCount; template <typename FunctionWrapperType> struct THE_PROXY { THE_PROXY(FunctionWrapperType* wrapper) : wrapper_(wrapper) {} template<typename T> THE_PROXY& operator=(T val) { // 编译期判断目标函数的参数个数,仅实例化符合条件的分支 if constexpr (MemberFuncArgCount<typename FunctionWrapperType::FuncType> == 0) { void* address_to_write_to = wrapper_->Set(); memcpy(address_to_write_to, &val, sizeof(val)); } else { wrapper_->Set(val); } return *this; } private: FunctionWrapperType* wrapper_; }; template<typename Function, typename ContainingClass> struct FunctionWrapper { using FuncType = Function; using Traits = MemberFuncTraits<Function>; FunctionWrapper(Function func, ContainingClass* c) : func_(func), containingClass_(c) {} THE_PROXY<FunctionWrapper> operator*() { return THE_PROXY(this); } private: template<class T> friend struct THE_PROXY; // 无参场景:调用返回void*的成员函数 void* Set() requires (Traits::ArgCount == 0) { return std::invoke(func_, containingClass_); } // 单参场景:调用带参数的成员函数,添加类型匹配检查 template<typename Arg> void Set(Arg arg) requires (Traits::ArgCount == 1) { static_assert(std::is_convertible_v<Arg, typename Traits::ArgType>, "参数类型不匹配"); std::invoke(func_, containingClass_, arg); } Function func_; ContainingClass* containingClass_; }; // 辅助函数:自动推导模板参数,简化包装器创建 template<typename Function, typename ContainingClass> auto make_function_wrapper(Function func, ContainingClass* c) { return FunctionWrapper<Function, ContainingClass>(func, c); } // 测试用例 struct MyStruct2 { void* Func() { return &n_; } void Func2(int n) { n_ = n; } // 特殊场景:返回void*但带参数的函数 void* Func3(int n) { n_ = n; return &n_; } private: int n_; }; int main() { MyStruct2 ms; // 自动推导模板参数,无需手动指定ArgType auto fw = make_function_wrapper(&MyStruct2::Func, &ms); auto fw2 = make_function_wrapper(&MyStruct2::Func2, &ms); auto fw3 = make_function_wrapper(&MyStruct2::Func3, &ms); *fw = 100; // 走memcpy写入逻辑(无参函数) *fw2 = 65; // 直接传参更新(单参函数) *fw3 = 200; // 直接传参更新(虽返回void*但带参数) }
关键细节说明
成员函数特性提取
通过模板特化解析成员函数指针的签名,精准获取参数个数,完全不依赖返回值判断,解决了"返回void*但带参数"的特殊场景问题。编译期分支控制
使用constexpr结合参数个数判断,确保只有符合条件的代码分支被编译,彻底避免了原代码中"invoke找不到匹配函数"的编译错误。编译期约束
给Set方法添加requires约束,进一步确保只有对应参数个数的Set方法会被实例化,强化类型安全。简化创建流程
新增make_function_wrapper辅助函数,自动推导模板参数,无需手动编写冗长的模板参数列表。
内容的提问来源于stack exchange,提问作者Wad
相关产品推荐
相关产品推荐

