由于函数指针需要提前知道提供了哪些参数,所以我不知道如何执行此操作。
基本上我想要一份工作清单。每个条目都是一个用特定参数调用的函数。即我想将foo(3, "abcd")
添加到工作列表,然后再添加bar(&h)
。也就是说,我事先不知道将添加哪些类型的函数。
稍后我将遍历此列表并执行指定的函数调用。
可以实施吗?
答案 0 :(得分:4)
您正在寻找std::function
和lambdas,或std::bind
。
std::function
是任意可调用的包装器。您可以在其中存储任何可以使用适当的参数调用operator()
的内容。
你可以存储的一件事是lambdas:你将调用和参数封装到非参数lambda中并调用它。
您可以存储的另一件事是std::bind
的结果。 std::bind
实际上是一个元函数:它将函数 f 和参数作为输入,并返回一个函数对象,其调用导致在参数上调用 f 。 / p>
以下是您如何将此应用于您的案例。常见设置:
std::vector<std::function<void()>> workList;
fillWorkList(workList);
for (auto& f : workList)
f();
以下是fillWorkList
的两种可能实现方式。一个std::bind
:
void fillWorkList(std::vector<std::function<void()>>& workList)
{
workList.push_back(std::bind(foo, 3, "abcd"));
workList.push_back(std::bind(bar, &h));
}
还有一个与lambdas:
void fillWorkList(std::vector<std::function<void()>>& workList)
{
workList.push_back([]() { foo(3, "abcd"); });
workList.push_back([]() { bar(&h); });
}
答案 1 :(得分:2)
std::function<void()>
表示可以调用的内容,并且不返回任何内容。
存储在其中的最清楚的东西是lambda。
std::function<void()> f = []{ foo(3, "abcd"); };
存储&#34;在名为foo( 3, "abcd" );
的{{1}}中致电std::function
。
我们可以建立一个列表 - f
或std::deque
- 并在以后调用它们。
您可以通过在std::vector
s中放置要捕获的内容来捕获lambda中的状态:
[]
将std::function<void()> g = [h]{ bar(&h); };
复制到lambda中,然后使用指向h
的指针调用bar
。有时你会希望h
是可变的:
h
您还可以使用lambda来存储对变量的引用。这是危险的,因为你负责终生,如果你将lambdas存储在std::function<void()> g = [h]()mutable{ bar(&h); };
中,然后将它们存储在容器中,那么生命周期可能并不简单。
在C ++ 14中,您甚至可以将表达式放在std::function
s。
[]
表现得像一个值。您可以使用std::function<void()>
调用它,就像调用带有签名()
的函数一样。
在技术上可以使用void()
而不是lambdas,但是std::bind
有许多奇怪的怪癖,生成的代码通常不太清楚,错误几乎总是不可读的。不要这样做。
您也可以使用自定义函数对象执行此操作。
std::bind
然后struct custom {
std::string s;
void operator()() const {
foo( 3, s );
}
};
是另一种方式,当std::function<void()> f = custom{ "abcd" };
foo
上3, std::string("abcd")
时,您将f()
调用f
。
答案 2 :(得分:0)
以下是允许使用add(foo, 3, "abcd")
的解决方案using a parameter pack和perfect forwarding:
#include <functional>
#include <string>
#include <iostream>
#include <vector>
#include <utility>
void foo(int val, std::string text) { std::cout << val << '\t' << text << '\n'; }
void bar(int* ptr) { std::cout << *ptr << '\n'; }
class Worklist {
public:
template <typename ...Args>
void add(Args&&... args) {
worklist.push_back(std::bind(std::forward<Args>(args)...));
}
void do_all()
{
for(auto& i : worklist) {
i();
}
}
std::vector<std::function<void(void)>> worklist;
};
int main()
{
int h{9};
Worklist worklist;
worklist.add(foo, 3, "abcd");
worklist.add(bar,&h);
worklist.do_all();
}
答案 3 :(得分:0)
可以编写一个接受lambdas作为任务的类,而不使用std::bind
或std::function
。
这里,std::unique_ptr
用于存储每个lambdas:
#include <string>
#include <iostream>
#include <vector>
#include <memory>
#include <utility>
void foo(int val, std::string text) { std::cout << val << '\t' << text << '\n'; }
void bar(int* ptr) { std::cout << *ptr << '\n'; }
class Generic_Callable {
public:
~Generic_Callable() = default;
virtual void call() = 0;
};
template <typename T>
class MyCallable : public Generic_Callable {
public:
MyCallable(T &&t) : ptr{std::make_unique<T>(std::move(t))} {}
void call() override
{
(*ptr)();
}
std::unique_ptr<T> ptr;
};
class Worklist {
public:
template <typename T>
void add(T &&t)
{
worklist.push_back(std::make_unique<MyCallable<T>>(std::move(t)));
}
void do_all() {
for(auto& i : worklist)
i->call();
}
std::vector<std::unique_ptr<Generic_Callable>> worklist;
};
int main()
{
int h{9};
Worklist worklist;
worklist.add([]() {foo(3, "abcd"); });
worklist.add([&h]() {bar(&h); });
worklist.do_all();
}