C++如何在map中存储带可变多类型参数的成员函数
问题:扩展Calculator类的成员函数映射,支持多参数成员函数
原代码如下:
#include <iostream> #include <map> #include <string> #include <math.h> using namespace std; class Calculator{ private: map<string, double (Calculator::*)(double)> mp = { {"ABS", &Calculator::abs}}; public: Calculator(){ cout << (this->*mp["ABS"])(-4.2655) << "\n"; } double abs(double ting){ return fabs(ting); } }; int main() { Calculator calc; return 0; }
当前代码中的map仅能存储带一个double类型参数的成员函数,现希望在该map中添加如sigma()这类带有int startVal、int endVal、string expression多类型参数的成员函数,请问如何基于现有代码结构实现?本人对template不太熟悉,想了解是否需要使用模板及具体用法。
解决方案
原有的map只能存储固定签名的成员函数指针,不同参数列表的成员函数指针属于不同类型,无法直接存入同一个map。要实现需求,需要用类型擦除机制统一存储类型,下面给出两种无需复杂模板的实用方案:
方案一:用std::variant存储不同签名的函数包装器(C++17及以上)
std::variant可以把多种不同类型打包成一种变体类型,我们用它来存放不同签名的std::function,每个成员函数通过lambda包装成对应签名的function存入map。
#include <iostream> #include <map> #include <string> #include <math.h> #include <functional> #include <variant> using namespace std; class Calculator{ private: // 定义变体类型,包含需要支持的两种函数签名 using FuncVariant = variant< function<double(Calculator&, double)>, function<double(Calculator&, int, int, string)> >; map<string, FuncVariant> mp = { {"ABS", function<double(Calculator&, double)>([](Calculator& calc, double val) { return calc.abs(val); })}, {"SIGMA", function<double(Calculator&, int, int, string)>([](Calculator& calc, int start, int end, string expr) { return calc.sigma(start, end, expr); })} }; public: Calculator(){ // 调用ABS函数:先判断变体类型,再执行 if (auto* abs_func = get_if<function<double(Calculator&, double)>>(&mp["ABS"])) { cout << (*abs_func)(*this, -4.2655) << "\n"; } // 调用SIGMA函数示例 if (auto* sigma_func = get_if<function<double(Calculator&, int, int, string)>>(&mp["SIGMA"])) { cout << (*sigma_func)(*this, 1, 5, "x") << "\n"; } } double abs(double ting){ return fabs(ting); } double sigma(int startVal, int endVal, string expression){ // 简单实现:计算start到end的整数累加 double sum = 0; for(int i = startVal; i <= endVal; ++i){ sum += i; } return sum; } }; int main() { Calculator calc; return 0; }
方案说明
- 新增
FuncVariant类型,枚举所有需要支持的函数签名对应的std::function - 每个成员函数用lambda包装,将成员函数调用转换为普通函数调用(传入
Calculator引用) - 调用时通过
get_if判断变体中的实际函数类型,安全执行调用
方案二:用统一签名的std::function+std::any传递参数(更灵活)
定义一个统一的函数签名,用std::any打包任意类型的参数,在lambda中解析参数并调用对应成员函数。这种方式无需每次新增函数都修改变体类型,扩展性更强。
#include <iostream> #include <map> #include <string> #include <math.h> #include <functional> #include <any> #include <stdexcept> using namespace std; class Calculator{ private: // 统一的通用函数类型:接收Calculator引用和任意参数列表(用vector<any>打包) using GenericFunc = function<double(Calculator&, const vector<any>&)>; map<string, GenericFunc> mp = { {"ABS", [](Calculator& calc, const vector<any>& args) { if(args.size() != 1) throw invalid_argument("ABS需要1个double类型参数"); return calc.abs(any_cast<double>(args[0])); }}, {"SIGMA", [](Calculator& calc, const vector<any>& args) { if(args.size() != 3) throw invalid_argument("SIGMA需要3个参数:int, int, string"); return calc.sigma( any_cast<int>(args[0]), any_cast<int>(args[1]), any_cast<string>(args[2]) ); }} }; public: Calculator(){ // 调用ABS try { cout << mp["ABS"](*this, {-4.2655}) << "\n"; } catch(const exception& e){ cerr << "调用ABS出错:" << e.what() << endl; } // 调用SIGMA try { cout << mp["SIGMA"](*this, {1, 5, "x"}) << "\n"; } catch(const exception& e){ cerr << "调用SIGMA出错:" << e.what() << endl; } } double abs(double ting){ return fabs(ting); } double sigma(int startVal, int endVal, string expression){ double sum = 0; for(int i = startVal; i <= endVal; ++i){ sum += i; } return sum; } }; int main() { Calculator calc; return 0; }
方案说明
- 用
GenericFunc统一所有函数的签名,参数中的vector<any>可以容纳任意数量、任意类型的参数 - 每个lambda负责校验参数数量和类型,通过
any_cast将std::any转换为具体类型后调用成员函数 - 加入异常处理,避免参数错误导致程序崩溃
关于模板的说明
上面两种方案都不需要复杂的模板语法,完全满足你的需求。如果以后需要实现更通用的参数打包逻辑,才需要用到可变参数模板,但当前场景下,上述两种方案足够简单实用。
内容的提问来源于stack exchange,提问作者Orange Splicer
相关产品推荐
相关产品推荐

