如何在C++容器中存储不同参数的类成员函数指针
解决方法
你想直接在/*FUNCTION ARGS*/位置用可变模板适配不同成员函数的签名是行不通的——因为std::unordered_map要求所有元素的value类型必须完全一致,而不同签名的std::function属于不同类型,没法直接放入同一个容器。
要实现存储不同签名成员函数指针的容器,有两种常用可行方案:
方案1:用std::variant封装不同的std::function类型
先定义每个成员函数对应的std::function类型,再用std::variant把它们打包成统一类型,作为容器的value类型。这种方式类型安全,是推荐的做法。
示例代码:
#include <unordered_map> #include <functional> #include <variant> #include <string> class MyClass { public: void funcOne(int a, float b) {} void funcTwo(std::string) {} void funcThree() {} }; // 定义每个成员函数对应的std::function类型(第一个参数为类实例引用) using Func1 = std::function<void(MyClass&, int, float)>; using Func2 = std::function<void(MyClass&, std::string)>; using Func3 = std::function<void(MyClass&)>; // 用variant统一所有函数类型 using AnyMemberFunc = std::variant<Func1, Func2, Func3>; // 最终容器定义 std::unordered_map<int, AnyMemberFunc> myMap; // 使用示例 int main() { // 将成员函数包装为lambda存入容器 myMap[1] = [](MyClass& obj, int a, float b) { obj.funcOne(a, b); }; myMap[2] = [](MyClass& obj, std::string s) { obj.funcTwo(std::move(s)); }; myMap[3] = [](MyClass& obj) { obj.funcThree(); }; MyClass obj; // 调用时用std::visit匹配对应函数类型 std::visit([&obj](auto&& func) { using FuncType = std::decay_t<decltype(func)>; if constexpr (std::is_same_v<FuncType, Func1>) { func(obj, 10, 3.14f); } else if constexpr (std::is_same_v<FuncType, Func2>) { func(obj, "test"); } else if constexpr (std::is_same_v<FuncType, Func3>) { func(obj); } }, myMap[1]); }
方案2:统一适配为带std::any参数的函数(灵活性高但类型不安全)
如果不想预先枚举所有函数类型,可以把所有成员函数适配成接收MyClass实例和任意数量std::any参数的std::function,但调用时需要手动校验并转换参数类型,存在类型错误风险。
示例代码:
#include <unordered_map> #include <functional> #include <any> #include <string> class MyClass { public: void funcOne(int a, float b) {} void funcTwo(std::string) {} void funcThree() {} }; // 统一的函数类型 using UnifiedFunc = std::function<void(MyClass&, const std::vector<std::any>&)>; // 容器定义 std::unordered_map<int, UnifiedFunc> myMap; // 使用示例 int main() { myMap[1] = [](MyClass& obj, const std::vector<std::any>& args) { // 手动校验参数数量和类型 if (args.size() != 2) return; int a = std::any_cast<int>(args[0]); float b = std::any_cast<float>(args[1]); obj.funcOne(a, b); }; myMap[2] = [](MyClass& obj, const std::vector<std::any>& args) { if (args.size() != 1) return; std::string s = std::any_cast<std::string>(args[0]); obj.funcTwo(std::move(s)); }; myMap[3] = [](MyClass& obj, const std::vector<std::any>&) { obj.funcThree(); }; MyClass obj; // 调用时传入参数的any数组 myMap[1](obj, {10, 3.14f}); }
关键说明
成员函数必须依赖类实例才能调用,所以不管哪种方案,std::function的签名里都需要包含MyClass的实例(引用或指针)作为第一个参数——这是成员函数和普通函数的核心区别。
内容的提问来源于stack exchange,提问作者cpp_hex
相关产品推荐
相关产品推荐

