在映射中存储具有不同签名的函数 我尝试用字符串> /COD>作为关键字,在C++中使用一个泛型方法,作为值>代码,但是我不知道这是不是可能的。我想这样做: void foo(int x, int y) { //do something } void bar(std::string x, int y, int z) { //do something } void main() { std::map<std::string, "Any Method"> map; map["foo"] = &foo; //store the methods in the map map["bar"] = &bar; map["foo"](1, 2); //call them with parameters I get at runtime map["bar"]("Hello", 1, 2); } void foo(整数x,整数y) { //做点什么 } 空栏(标准::字符串x,整数y,整数z) { //做点什么 } void main() { 地图; map[“foo”]=&foo;//将方法存储在map中 映射[“条”]=&bar; map[“foo”](1,2);//使用运行时获得的参数调用它们 地图[“酒吧](“你好”,1,2); }

在映射中存储具有不同签名的函数 我尝试用字符串> /COD>作为关键字,在C++中使用一个泛型方法,作为值>代码,但是我不知道这是不是可能的。我想这样做: void foo(int x, int y) { //do something } void bar(std::string x, int y, int z) { //do something } void main() { std::map<std::string, "Any Method"> map; map["foo"] = &foo; //store the methods in the map map["bar"] = &bar; map["foo"](1, 2); //call them with parameters I get at runtime map["bar"]("Hello", 1, 2); } void foo(整数x,整数y) { //做点什么 } 空栏(标准::字符串x,整数y,整数z) { //做点什么 } void main() { 地图; map[“foo”]=&foo;//将方法存储在map中 映射[“条”]=&bar; map[“foo”](1,2);//使用运行时获得的参数调用它们 地图[“酒吧](“你好”,1,2); },c++,C++,可能吗?如果是,我如何实现这一点?您不能将具有不同签名的函数存储在像map这样的容器中,无论您将它们存储为函数指针还是std::function。关于函数签名的信息在这两种情况下都是唯一的 映射中的值的类型是一种,这意味着存储在其中的对象都属于同一类型 因此,如果您的函数具有所有相同的签名,那么这很容易,否则,您必须放弃类型安全性,开始进入一个非常危险的领域。 在其中,您可以删除有关存储在地图中的函数的类型信息。 这可以转化为类似于map的东西,您能做的最多(我这里不能说最好)就是使用签名擦除。

可能吗?如果是,我如何实现这一点?

您不能将具有不同签名的函数存储在像
map
这样的容器中,无论您将它们存储为函数指针还是
std::function
。关于函数签名的信息在这两种情况下都是唯一的

映射
中的
的类型是一种,这意味着存储在其中的对象都属于同一类型

因此,如果您的函数具有所有相同的签名,那么这很容易,否则,您必须放弃类型安全性,开始进入一个非常危险的领域。 在其中,您可以删除有关存储在地图中的函数的类型信息。 这可以转化为类似于
map

的东西,您能做的最多(我这里不能说最好)就是使用签名擦除。这意味着将指向函数的指针转换为公共签名类型,然后在使用它们之前将它们转换回正确的签名

这只能在非常特殊的用例中完成(我无法想象现实世界中的情形),而且是非常不安全的:没有任何东西可以阻止您将错误的参数传递给函数。简而言之:在现实世界的代码中永远不要这样做

尽管如此,以下是一个工作示例:

#include <iostream>
#include <string>
#include <map>

typedef void (*voidfunc)();

void foo(int x, int y)
{
    std::cout << "foo " << x << " " << y << std::endl;
}

void bar(std::string x, int y, int z)
{
    std::cout << "bar " << x << " " << y << " " << z << std::endl;
}

int main()
{
    std::map<std::string, voidfunc> m;
    m["foo"] = (voidfunc) &foo;
    m["bar"] = (voidfunc)& bar;
    ((void(*)(int, int)) m["foo"])(1, 2);
    ((void(*)(std::string, int, int)) m["bar"])("baz", 1, 2);
    return 0;
}

我在标准中找不到这是否会调用未定义的行为,因为很少有关于函数指针转换的内容,但我很确定所有通用编译器都接受这一点,因为它只涉及函数指针强制转换。

您可以在容器中键入擦除函数类型,然后提供一个模板
操作符()
。这将抛出
std::bad\u any\u cast
,如果你弄错了

注意:由于类型擦除,您必须在调用站点指定完全匹配的参数,例如,
std::function
不同于
std::function
,即使两者都可以使用类似
“Hello”
的值调用

#包括
#包括
#包括
#包括
#包括
模板
结构AnyCallable
{
AnyCallable(){}
模板
AnyCallable(F&&fun):AnyCallable(std::function(fun)){}
模板
AnyCallable(std::function fun):m_any(fun){}
模板
Ret运算符()(Args&&…Args)
{ 
返回std::invoke(std::any_cast(m_any),std::forward(args)…);
}
std::任意m_任意;
};
模板
结构AnyCallable
{
AnyCallable(){}
模板
AnyCallable(F&&fun):AnyCallable(std::function(fun)){}
模板
AnyCallable(std::function fun):m_any(fun){}
模板
void运算符()(Args&&…Args)
{ 
std::invoke(std::any_cast(m_any)、std::forward(args)…);
}
std::任意m_任意;
};
无效foo(整数x,整数y)
{

std::cout然后你可以阅读一份文档。
&foo
&bar
是不同的类型。第一种类型是
(void*)(int,int)
,第二种类型是
(void*)(std::string,int,int)
。所以你需要有不同的想法。顺便问一句,你最初的任务是什么?当你试图调用
map[“foo”](“你好”)时会发生什么
?也许你应该看看@suraznegi,这是非常不安全的…当将函数指针保存为
void*
时,你必须在调用它之前将其转换回实际签名,至少在我的测试中是这样!所以我认为使用映射来存储具有不同签名的函数指针没有多大意义。最好使用虚拟函数或重载方法。@xander是的,为了使用它,您必须将其强制转换回。使用类可以获得相同的结果,但您并没有真正存储函数。这就是为什么我的答案中没有包含它。不过,这一点很好。您可以始终使用std::任何std::函数…我不会感到惊讶,这具有ame存在潜在的UB问题,但至少通过使用标准库提供了定义的外观constructions@buttonsrtoysiirc VS2017不完全兼容,它可能缺少
std::function
map[“foo”](1,2)
更改为
int n=1;map[“foo”](n,2)的演绎指南
在运行时失败,抛出一个
bad\u any\u cast
。调用被转换为
std::function
,而
any
有一个
std::function
。这使得映射使用起来非常棘手——有什么办法可以缓解这个问题吗?(我在gcc-mp-8(MacPorts gcc8.2.0)上使用c++2a模式)8.2.0--不确定这是否是一个因素。@RandomBits不,这种技术非常脆弱。您可以通过显式命名声明类型来缓解它,例如
map[“foo”].operator()(n,m);
,但这很难看,而且仍然很容易获得wrong@Caleth这在
AnyCallable(std::function(fun)){
是。它说的是“std::function”:使用类模板需要模板参数列表。@Caleth:如何将返回值也泛化,而不是将其硬编码为
void
?也可以在使用变量时只写入
int i=1;map[“foo”].operator()((int)i,2)
似乎有效。最后一次强制转换是必需的,否则它将无法编译。此解决方案仍然非常不安全,并且不像“正常”函数调用那样运行。
foo 1 2
bar baz 1 2
#include <any>
#include <functional>
#include <map>
#include <string>
#include <iostream>

template<typename Ret>
struct AnyCallable
{
    AnyCallable() {}
    template<typename F>
    AnyCallable(F&& fun) : AnyCallable(std::function(fun)) {}
    template<typename ... Args>
    AnyCallable(std::function<Ret(Args...)> fun) : m_any(fun) {}
    template<typename ... Args>
    Ret operator()(Args&& ... args) 
    { 
        return std::invoke(std::any_cast<std::function<Ret(Args...)>>(m_any), std::forward<Args>(args)...); 
    }
    std::any m_any;
};

template<>
struct AnyCallable<void>
{
    AnyCallable() {}
    template<typename F>
    AnyCallable(F&& fun) : AnyCallable(std::function(fun)) {}
    template<typename ... Args>
    AnyCallable(std::function<void(Args...)> fun) : m_any(fun) {}
    template<typename ... Args>
    void operator()(Args&& ... args) 
    { 
        std::invoke(std::any_cast<std::function<void(Args...)>>(m_any), std::forward<Args>(args)...); 
    }
    std::any m_any;
};

void foo(int x, int y)
{
   std::cout << "foo" << x << y << std::endl;
}

void bar(std::string x, int y, int z)
{
   std::cout << "bar" << x << y << z << std::endl;
} 

using namespace std::literals;

int main()
{
   std::map<std::string, AnyCallable<void>> map;

   map["foo"] = &foo;      //store the methods in the map
   map["bar"] = &bar;

   map["foo"](1, 2);       //call them with parameters I get at runtime
   map["bar"]("Hello, std::string literal"s, 1, 2);
   //map["bar"]("Hello, const char *literal", 1, 2); // bad_any_cast
   map["bar"].operator()<std::string, int, int>("Hello, const char *literal", 1, 2); // explicit template parameters

   return 0;
}