如何通过模板化实现Wrapper类高效转发Worker类成员函数调用
问题:批量实现Wrapper对Worker同名方法的参数转发
现有代码结构如下:
class Wrapper { public: AnyType Foo(std::uint64_t unique_id, Args&&... args) { return worker_map[unique_id]->Foo(std::forward<Args>(args)...); } AnotherType Bar(std::uint64_t unique_id, Args&&... args) { return worker_map[unique_id]->Bar(std::forward<Args>(args)...); } private: std::unordered_map<std::uint64_t, std::unique_ptr<Worker>> worker_map; }; class Worker { public: AnyType Foo(<some arguments>); AnotherType Bar(<another arguments>); // 更多成员函数 };
Wrapper的所有公有成员函数逻辑一致:通过unique_id从worker_map中匹配对应的Worker实例,再把剩余参数转发给Worker中同名的成员函数。但Worker已有大量成员函数,且后续会持续增删改,希望通过模板化方法名的方式减少重复代码。
可行解决方案
方案1:模板成员函数+成员函数指针
定义一个通用的模板转发函数,接收Worker的成员函数指针作为参数,统一处理实例查找和参数转发:
#include <functional> #include <memory> #include <unordered_map> #include <stdexcept> class Worker { public: int Foo(int a, double b) { return static_cast<int>(a + b); } std::string Bar(const std::string& s) { return "Hello, " + s; } // 其他成员函数... }; class Wrapper { public: // 转发非const成员函数 template<typename Ret, typename... Args, typename... CallArgs> Ret invoke(std::uint64_t unique_id, Ret (Worker::*func)(Args...), CallArgs&&... args) { auto it = worker_map.find(unique_id); if (it == worker_map.end()) { throw std::invalid_argument("Invalid unique_id"); } return (it->second.get()->*func)(std::forward<CallArgs>(args)...); } // 转发const成员函数(如果需要) template<typename Ret, typename... Args, typename... CallArgs> Ret invoke(std::uint64_t unique_id, Ret (Worker::*func)(Args...) const, CallArgs&&... args) const { auto it = worker_map.find(unique_id); if (it == worker_map.end()) { throw std::invalid_argument("Invalid unique_id"); } return (it->second.get()->*func)(std::forward<CallArgs>(args)...); } private: std::unordered_map<std::uint64_t, std::unique_ptr<Worker>> worker_map; };
使用示例:
Wrapper w; // 假设已向worker_map添加id为1的Worker实例 w.invoke(1, &Worker::Foo, 5, 3.14); w.invoke(1, &Worker::Bar, "World");
这种方式无需为每个Worker方法单独编写Wrapper成员函数,新增Worker方法时直接通过invoke调用即可,完全不需要修改Wrapper代码。
方案2:C++20 模板化方法名(更直观的调用方式)
如果项目使用C++20,可以利用auto模板参数和std::invoke实现更简洁的调用:
#include <functional> #include <memory> #include <unordered_map> #include <stdexcept> #include <utility> class Worker { public: int Foo(int a, double b) { return static_cast<int>(a + b); } std::string Bar(const std::string& s) { return "Hello, " + s; } // 其他成员函数... }; class Wrapper { public: template<auto Func, typename... Args> auto call(std::uint64_t unique_id, Args&&... args) { auto it = worker_map.find(unique_id); if (it == worker_map.end()) { throw std::invalid_argument("Invalid unique_id"); } return std::invoke(Func, *it->second, std::forward<Args>(args)...); } // const版本 template<auto Func, typename... Args> auto call(std::uint64_t unique_id, Args&&... args) const { auto it = worker_map.find(unique_id); if (it == worker_map.end()) { throw std::invalid_argument("Invalid unique_id"); } return std::invoke(Func, *it->second, std::forward<Args>(args)...); } private: std::unordered_map<std::uint64_t, std::unique_ptr<Worker>> worker_map; };
使用示例:
Wrapper w; w.call<&Worker::Foo>(1, 5, 3.14); w.call<&Worker::Bar>(1, "World");
这种方式调用时更直观,模板参数直接指定Worker的成员函数,返回值由编译器自动推导,代码更简洁。
关键注意事项
- 必须处理
unique_id不存在的情况,避免空指针访问导致崩溃 - 使用
std::forward保证参数的完美转发,保留参数的左值/右值属性 - 如果Worker包含const成员函数,需要单独实现const版本的转发函数
内容的提问来源于stack exchange,提问作者Stephen
相关产品推荐
相关产品推荐

