如何将带不同参数的类成员函数存入映射并可变参数调用?
问题描述
我有一个包含若干参数不同的私有成员函数的类,希望将这些函数存入一个map中,再通过一个接受可变参数的统一函数来调用它们。我可以接受调用参数不匹配时抛出bad_any_cast异常(例如调用v.call("Bob", 1)的情况)。某StackOverflow帖子的解决方案和我的需求非常接近,但我无法将其应用于成员函数、std::function或lambda表达式(三者任一均可)。
目前的问题在于calls映射的初始化以及如何调用map中std::any存储的函数。我可以使用C++17及以下版本的特性。
#include <any> #include <iostream> #include <map> using namespace std; class VarCall { public: VarCall() : calls({{"Alice", foo}, {"Bob", bar}, {"Charlie", baz}}) {} template <typename... Args> void call(const string& what, Args... args) { any_cast<???>(calls[what])(args...); } private: void foo() { cout << "foo()" << endl; } void bar(const string& s) { cout << "bar(" << s << ")" << endl; } void baz(int i) { cout << "baz(" << i << ")" << endl; } map<string, any> calls; }; int main() { VarCall v; v.call("Alice"); //prints foo() v.call("Bob", "2"); //prints bar(2) v.call("Charlie", 1); //prints baz(1) return 0; }
解决方案
核心问题在于成员函数必须依附类实例(this指针)才能调用,不能直接将成员函数指针存入std::any。需要先把this绑定到成员函数上,包装成可直接调用的对象,再存入容器。
方法1:用Lambda捕获this+std::function包装
这是最直观的实现方式,通过lambda捕获当前类的this指针,把成员函数调用包装成独立的可调用对象,再用std::function统一类型后存入std::any。
修改后的完整代码:
#include <any> #include <iostream> #include <map> #include <functional> using namespace std; class VarCall { public: VarCall() { // 用lambda捕获this,将成员函数包装为对应签名的std::function calls["Alice"] = function<void()>([this]() { this->foo(); }); calls["Bob"] = function<void(const string&)>([this](const string& s) { this->bar(s); }); calls["Charlie"] = function<void(int)>([this](int i) { this->baz(i); }); } template <typename... Args> void call(const string& what, Args&&... args) { // 根据传入参数的签名,将std::any转换为对应std::function并调用 any_cast<function<void(Args...)>>(calls[what])(forward<Args>(args)...); } private: void foo() { cout << "foo()" << endl; } void bar(const string& s) { cout << "bar(" << s << ")" << endl; } void baz(int i) { cout << "baz(" << i << ")" << endl; } map<string, any> calls; }; int main() { VarCall v; v.call("Alice"); // 输出 foo() v.call("Bob", "2"); // 输出 bar(2) v.call("Charlie", 1); // 输出 baz(1) // 故意传错参数会抛出bad_any_cast异常,符合需求 // v.call("Bob", 1); return 0; }
关键细节说明
- 成员函数的包装:
lambda捕获this后,相当于把成员函数和当前类实例绑定在一起,生成的可调用对象可以直接执行,无需额外传入this。 std::any的类型转换:
在call模板函数中,根据传入的参数类型推导目标std::function的签名,用any_cast完成类型转换。如果参数签名不匹配,any_cast会抛出bad_any_cast异常。- 完美转发:
使用std::forward<Args>(args)...转发参数,避免不必要的拷贝,保持参数的原类型特性(比如左值/右值属性)。
方法2:用std::bind绑定this指针
也可以用std::bind替代lambda来绑定this和成员函数,效果完全一致:
// 构造函数中的初始化代码替换为: calls["Alice"] = bind(&VarCall::foo, this); calls["Bob"] = bind(&VarCall::bar, this, placeholders::_1); calls["Charlie"] = bind(&VarCall::baz, this, placeholders::_1);
std::bind生成的可调用对象同样可以存入std::any,call函数的转换逻辑和方法1完全相同。
注意事项
- 必须保证
call传入的参数签名和目标成员函数完全匹配,否则会触发bad_any_cast异常。 - 由于我们绑定了
this指针,要确保VarCall实例的生命周期长于calls中的可调用对象(这里calls是类成员,自然满足)。
内容的提问来源于stack exchange,提问作者merula
相关产品推荐
相关产品推荐

