如何存储类成员函数并实现可变参数模板函数调用及返回结果?
问题描述
需要实现以下功能:
- 将多个类的成员函数存入以标识符为索引的容器中
- 实现可变参数模板函数
processArgs,完成:- 接收标识符和一组参数
- 通过标识符在容器中找到对应函数
- 用提供的参数调用该函数
- 支持调用const成员函数
- 额外需求:让
processArgs返回被调用函数的执行结果
示例代码如下:
#include <iostream> #include <unordered_map> class MyClass { public: void someFunc(const char* str, int a, double b) { std::cout << str << " " << a << " " << b << std::endl; } float magnitude(double x, double y, double z) { return sqrt(x*x+y*y+z*z); } std::string toStr(float x) { return std::to_string(x); } }; using fnId = int; std::unordered_map<fnId, Ret (Classes::*)(Args...)> functions; // This is going to be filled with functions template<typename... Args> void processArgs(int fnId, Args... args) { // This function will retrieve a function by fnId, and forward args in specific order/type to that specific function, for that specific object // so it will be like obj->someFunc(args...); } int main() { MyClass obj; // Assuming the container is filled with functions functions[0] = &MyClass::someFunc; functions[1] = &MyClass::magnitude; functions[2] = &MyClass::toStr; // This is how i execute processArgs(0, "text", 30, 3.34); processArgs(1, 2.0, 4.0, 6.0); processArgs(2, 3.14159); return 0; }
解决方案
1. 容器类型改造
不同成员函数的返回值、参数、所属类存在差异,直接存储成员函数指针无法兼容。改用std::unordered_map<fnId, std::any>作为容器,用std::any存储不同签名的std::function对象,实现统一存储。
2. 成员函数注册方式
注册时通过std::bind绑定对象实例和成员函数,将成员函数转换为可直接调用的std::function,再存入容器。普通成员函数和const成员函数都可以用这种方式处理:
// 注册普通成员函数 functions[0] = std::function<void(const char*, int, double)>( std::bind(&MyClass::someFunc, &obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); // 注册const成员函数 functions[3] = std::function<int()>( std::bind(&ConstClass::getValue, &constObj) );
3. 实现processArgs函数
通过模板重载分别处理有返回值和无返回值的情况,利用std::any_cast将容器中的std::any转换为对应类型的std::function,再转发参数调用并返回结果:
template<typename Ret, typename... Args> Ret processArgs(fnId id, Args&&... args) { auto& func_any = functions.at(id); auto func = std::any_cast<std::function<Ret(Args...)>>(&func_any); if (!func) { throw std::runtime_error("Function type mismatch"); } return (*func)(std::forward<Args>(args)...); } template<typename... Args> void processArgs(fnId id, Args&&... args) { auto& func_any = functions.at(id); auto func = std::any_cast<std::function<void(Args...)>>(&func_any); if (!func) { throw std::runtime_error("Function type mismatch"); } (*func)(std::forward<Args>(args)...); }
完整可运行代码
#include <iostream> #include <unordered_map> #include <functional> #include <any> #include <cmath> #include <stdexcept> class MyClass { public: void someFunc(const char* str, int a, double b) { std::cout << str << " " << a << " " << b << std::endl; } float magnitude(double x, double y, double z) const { return sqrt(x*x + y*y + z*z); } std::string toStr(float x) { return std::to_string(x); } }; class ConstClass { public: int getValue() const { return 42; } }; using fnId = int; std::unordered_map<fnId, std::any> functions; template<typename Ret, typename... Args> Ret processArgs(fnId id, Args&&... args) { auto& func_any = functions.at(id); auto func = std::any_cast<std::function<Ret(Args...)>>(&func_any); if (!func) { throw std::runtime_error("Function type mismatch"); } return (*func)(std::forward<Args>(args)...); } template<typename... Args> void processArgs(fnId id, Args&&... args) { auto& func_any = functions.at(id); auto func = std::any_cast<std::function<void(Args...)>>(&func_any); if (!func) { throw std::runtime_error("Function type mismatch"); } (*func)(std::forward<Args>(args)...); } int main() { MyClass obj; ConstClass constObj; // 注册各类成员函数 functions[0] = std::function<void(const char*, int, double)>( std::bind(&MyClass::someFunc, &obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); functions[1] = std::function<float(double, double, double)>( std::bind(&MyClass::magnitude, &obj, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) ); functions[2] = std::function<std::string(float)>( std::bind(&MyClass::toStr, &obj, std::placeholders::_1) ); functions[3] = std::function<int()>( std::bind(&ConstClass::getValue, &constObj) ); // 调用测试 processArgs(0, "text", 30, 3.34); float mag = processArgs<float>(1, 2.0, 4.0, 6.0); std::cout << "Magnitude: " << mag << std::endl; std::string str = processArgs<std::string>(2, 3.14159f); std::cout << "String: " << str << std::endl; int val = processArgs<int>(3); std::cout << "Const value: " << val << std::endl; return 0; }
关键说明
std::any作为容器值类型,兼容不同签名的函数对象std::bind完成成员函数与对象实例的绑定,消除成员函数的隐式this参数- 模板化的
processArgs支持参数转发,保证调用效率 - 重载版本分别处理无返回值和有返回值的函数调用,同时兼容const成员函数
内容的提问来源于stack exchange,提问作者Kitiara
相关产品推荐
相关产品推荐

