You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将带不同参数的类成员函数存入映射并可变参数调用?

问题描述

我有一个包含若干参数不同的私有成员函数的类,希望将这些函数存入一个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;
}

关键细节说明

  1. 成员函数的包装:
    lambda捕获this后,相当于把成员函数和当前类实例绑定在一起,生成的可调用对象可以直接执行,无需额外传入this。
  2. std::any的类型转换:
    在call模板函数中,根据传入的参数类型推导目标std::function的签名,用any_cast完成类型转换。如果参数签名不匹配,any_cast会抛出bad_any_cast异常。
  3. 完美转发:
    使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 00:32:51