如何实现可存储任意返回类型、任意参数数量及类型函数指针的std::map?(附Boost.Variant尝试代码及问题)
解决存储任意签名函数指针到std::map的问题
嘿,我来帮你搞定这个问题!先看看你当前代码里的几个关键问题:
- typedef不能重复定义(重载):C++里不允许同名的typedef多次定义,你第二个
typedef MultiType(*FnPtr)(MultiType, MultiType, MultiType);直接覆盖了第一个,导致str函数(签名是MultiType(std::string))和FnPtr类型不匹配,根本没法塞进map里。 - std::map的value类型必须统一:同一个map里的所有value必须是同一种类型,你没法直接把不同签名的函数指针混存进去。
下面给你两种基于Boost库的可行方案,都是贴合你的需求来做的:
方案一:用boost::variant存储不同签名的函数指针
我们可以把各种不同类型的函数指针塞进boost::variant这个“万能容器”里,让map的value类型变成这个variant,这样就能存多种签名的函数了。
#include <boost/variant.hpp> #include <map> #include <iostream> #include <string> using namespace boost; typedef boost::variant<int, float, double, std::string> MultiType; // 先给不同签名的函数指针分别定义类型 typedef MultiType (*Fn2Args)(MultiType, MultiType); typedef MultiType (*Fn1StrArg)(std::string); // 用variant把这些类型统一起来 typedef boost::variant<Fn2Args, Fn1StrArg> AnyFnPtr; // 你的原函数完全不用改 MultiType add(MultiType i, MultiType j) { return get<int>(i) + get<int>(j);} MultiType sub(MultiType i, MultiType j) { return get<int>(i) - get<int>(j);} MultiType str(std::string s) { return s; } int main() { std::map<std::string, AnyFnPtr> myMap; myMap["add"] = add; myMap["sub"] = sub; myMap["str"] = str; // 调用的时候得先判断variant里的实际函数类型 std::string op("add"); // 用boost::get尝试获取对应的函数指针 if (Fn2Args* fn = boost::get<Fn2Args>(&myMap[op])) { int res = get<int>((*fn)(2, 3)); std::cout << res << std::endl; } // 调用str函数的例子 op = "str"; if (Fn1StrArg* fn = boost::get<Fn1StrArg>(&myMap[op])) { std::string res = get<std::string>((*fn)("hello there")); std::cout << res << std::endl; } }
这个方案的好处是不用修改你原来的函数,但调用的时候需要做类型判断,稍微有点麻烦。
方案二:用boost::function统一封装所有函数
如果想让调用逻辑更简洁,可以把所有函数都封装成接受MultiType向量、返回MultiType的统一类型,用boost::function来做这个包装。
#include <boost/variant.hpp> #include <boost/function.hpp> #include <map> #include <iostream> #include <string> #include <vector> #include <stdexcept> using namespace boost; typedef boost::variant<int, float, double, std::string> MultiType; // 定义统一的函数包装类型 typedef boost::function<MultiType(const std::vector<MultiType>&)> AnyFn; // 给每个原函数写个适配器,把参数转换成向量形式 MultiType add_adapter(const std::vector<MultiType>& args) { if (args.size() != 2) throw std::invalid_argument("add需要2个参数"); return get<int>(args[0]) + get<int>(args[1]); } MultiType sub_adapter(const std::vector<MultiType>& args) { if (args.size() != 2) throw std::invalid_argument("sub需要2个参数"); return get<int>(args[0]) - get<int>(args[1]); } MultiType str_adapter(const std::vector<MultiType>& args) { if (args.size() != 1) throw std::invalid_argument("str需要1个参数"); return get<std::string>(args[0]); } int main() { std::map<std::string, AnyFn> myMap; myMap["add"] = add_adapter; myMap["sub"] = sub_adapter; myMap["str"] = str_adapter; // 调用add,直接传参数向量就行 std::string op("add"); int res = get<int>(myMap[op]({2, 3})); std::cout << res << std::endl; // 调用str op = "str"; std::string str_res = get<std::string>(myMap[op]({"hello stackoverflow"})); std::cout << str_res << std::endl; }
这个方案调用的时候特别统一,不用做类型判断,但需要给每个原函数写个适配器。如果你的函数很多,可以考虑用模板来简化适配器的编写,避免重复代码。
额外提一句:如果你用的是C++11及以上版本,也可以用标准库的std::function和std::variant替代Boost的对应组件,用法几乎一模一样。
内容的提问来源于stack exchange,提问作者Anees
相关产品推荐
相关产品推荐

