使用任何参数创建 std::functions 的 unordered_map?

Creating an unordered_map of std::functions with any arguments?

我正在尝试创建 std::functions 的无序地图。其中键是一个字符串,您将在其中查找要调用的函数,函数是值。

我写过一个小程序:

#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>

void func1()
{
  std::cout << "Function1 has been called." << std::endl;
}

int doMaths(int a)
{
  return a + 10;
}

int main()
{
  std::unordered_map<std::string,std::function<void()>> myMap;

  myMap["func1"] = func1;

}

这编译得很好,我可以调用函数(但我不确定这是否是正确的方法)通过放置:

auto mapIter = myMap.find("func1");
auto mapVal = mapIter->second;
mapVal();

然后调用函数,但是我认为这是以创建函数的新副本为代价的?如果我错了,请纠正我。

但是,如果我尝试这样做:myMap["doMaths"] = doMaths; 我会收到编译器错误,因为 myMap 中的值是 std::function<void()>> 而不是 std::function<int(int)>>。当我这样做时,我确实得到了这个编译:myMap["doMaths"] = std::bind(doMaths,int()); 但是我不知道它实际上做了什么。当我尝试以与 func1 相同的方式调用它时,出现编译器错误。

所以我想我有两个问题:

如何创建一个 unordered_map 可以接受任何类型函数的值?以及如何在不复制函数的情况下调用地图中的函数?

正如 eerorika 所说,您可以使用 std::any 的地图来做到这一点。下面是一些示例代码,还显示了如何调用存储在映射中的函数指针:

#include <iostream>
#include <unordered_map>
#include <string>
#include <any>

void func1()
{
    std::cout << "Function1 has been called.\n";
}

int doMaths(int a)
{
    std::cout << "doMaths has been called, a = " << a << "\n";
    return a + 10;
}

int main()
{
    std::unordered_map<std::string,std::any> myMap;

    myMap["func1"] = func1;
    auto mapIter = myMap.find("func1");
    std::any_cast <void (*) ()> (mapIter->second) ();
    
    myMap["doMaths"] = doMaths;
    mapIter = myMap.find("doMaths");
    int result = std::any_cast <int (*) (int)> (mapIter->second) (5);
    std::cout << result;
}

Live demo

std::any_cast 如果类型(或在本例中为函数签名)不匹配,将在运行时抛出 std::bad_any_cast 异常。

请注意:std::any 需要 C++17,参见:https://en.cppreference.com/w/cpp/utility/any

如果您知道函数的类型是 std::function<void(void)>,请将其放在 std::unordered_map<std::string,std::function<void()>> 中,然后在那里查找。

如果您知道函数的类型是 std::function<int(double, char)>,请将其放入 std::unordered_map<std::string,std::function<int(double, char)>>,然后在那里查找。

如果你不知道你的函数的类型,你就无法使用它,所以将它存储在一个映射中也是没有用的。

如果你有不止一种类型的功能,也就拥有不止一张地图。变量模板(或带有静态映射变量的函数模板)可以帮助拥有任意数量的此类映射,而无需复制任何代码。当然这样你只能有一张全球地图。可以管理此类地图集合的 class 可能会涉及更多,但只是一点点。

就像 Paul 所说的,但使用 std::function 语法。您可以使用它在您的地图中放置具有任何签名的函数,甚至是 lambdas :D

#include <vector>
#include <iostream>
#include <functional>
#include <map>
#include <any>

int doMaths(int a)
{
    std::cout << "doMaths has been called, a = " << a << "\n";
    return a + 10;
}

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

  map["foo"] = std::function<int(int)>([](int a) { return a * 2; });

  map["bar"] = std::function<int(int, int)>([](int a, int b) { return a + b; });

  map["maths"] = std::function<int(int)>(doMaths);

  int a = std::any_cast<std::function<int(int)>>(map["foo"])(4);

  int b = std::any_cast<std::function<int(int, int)>>(map["bar"])(4, 5);

  int c = std::any_cast<std::function<int(int)>>(map["maths"])(5);

  std::cout << a << " " << b << " " << c <<std::endl;
}

投射时要小心,您需要知道正确的签名和 return 类型。