我想知道是否有一种方法可以在具有现代C ++的标准容器(std :: map)中存储具有不同签名的功能对象(函数,回调,...)。管理容器的库不知道其客户"将使用哪些签名。
我的需求与此处公开的内容相同:How to store functional objects with different signatures in a container?,此解决方案https://stackoverflow.com/a/8304873/4042960对我来说非常完美:我只想在没有提升的情况下做同样的事情。据我所知,没有std :: any。对我来说最好的解决方案是存储std :: function而不专门设置它们,但如果可能,我不知道该怎么做。
编辑:
通过你给我的答案,我写了这个例子:
#include <map>
#include <memory>
#include <functional>
#include <string>
#include <iostream>
#include <stdexcept>
class FunctionMap
{
struct Base {
virtual ~Base() {}
};
template<class R, class... Args>
struct Func : Base
{
std::function<R(Args...)> f;
};
std::map<std::string, std::shared_ptr<Base> > _map;
public:
template<class R, class... Args>
void store(const std::string &key, const std::function<R(Args...)> &f) {
auto pfunc = std::make_shared<Func<R, Args...> >();
pfunc->f = f;
_map.insert(std::make_pair(key, pfunc));
}
template<class R, class... Args>
std::function<R(Args...)> get(const std::string &key) {
auto pfunc = std::dynamic_pointer_cast<Func<R, Args...> >(_map[key]);
if (pfunc)
return pfunc->f;
else
throw std::runtime_error("Bad type for function's parameters");
}
};
// test
int plus(int a, int b) { return a+b; }
double multiplies(double x, double y) { return x*y; }
int main()
{
FunctionMap fm;
fm.store("plus", std::function<int(int, int)>(&plus));
fm.store("multiplies", std::function<double(double, double)>(&multiplies));
// fm.store("square", std::bind(&multiplies, std::placeholders::_1, std::placeholders::_1));
std::cout << "5 + 3 = " << fm.get<int, int, int>("plus")(5, 3) << std::endl;
std::cout << "5 * 3 = " << fm.get<double, double, double>("multiplies")(5.0, 3.0) << std::endl;
return 0;
}
这很好用,但我想稍微改进一下:
1)我希望能够使用std::bind
:fm.store("square", std::bind(&multiplies, std::placeholders::_1, std::placeholders::_1));
但目前无法编译;
2)我想使用fm.get<int (int, int)>("plus")
代替fm.get<int, int, int>("plus")
,但我不知道该怎么做。
非常感谢你的帮助!
答案 0 :(得分:1)
您可以自己编写any
。如果没有所有编译器的解决方法和内容,boost::any
可以用大约30行代码编写。
答案 1 :(得分:0)
函数对象与任何其他类型的对象没有任何区别,因此适用于对象的任何内容都适用于函数对象。
因此,您希望在地图中存储不同类型的(函数)对象。这通常通过将(智能)指针存储到基类来完成,其中每个派生类都拥有您想要存储的自己的对象类型。
struct Base {
virtual ~Base(){}
};
template <typename A>
struct Object : Base {
A value;
};
这是你的基本穴居人的提升::任何。您的客户做这样的事情:
Base* b = mymap["foo"];
dynamic_cast<Object<void(*)(int)>*>(b)->val(123);
但当然要进行适当的检查。