通过带有移动捕获的lambda起作用 [英] Passing a lambda with moved capture to function
问题描述
我最近为一个难以找到的bug苦苦挣扎.我试图将lambda传递给带有std::function
对象的函数. Lambda正在捕获不可复制的对象.
I recently struggled with a bug hard to find for me. I tried to pass a lambda to a function taking a std::function
object. The lambda was capturing a noncopyable object.
我发现,显然所有传球之间都必须有一些副本.之所以得出这个结果,是因为我总是以error: use of deleted function
错误结尾.
I figured out, obviously some copy must happen in between all the passings. I came to this result because I always ended in an error: use of deleted function
error.
以下是产生此错误的代码:
Here is the code which produces this error:
void call_func(std::function<void()> func)
{
func();
}
int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
return 0;
}
我通过将std::fstream
对象封装在std::shared_ptr
对象中来解决此问题.效果很好,但我认为可能会有一种更性感的方式来实现此目的.
I solved this by capseling the std::fstream
object in an std::shared_ptr
object. This is working fine, but I think there may be a more sexy way to do this.
我现在有两个问题:
- 为什么会出现此错误?
- 我的想法:我在
for
循环中生成了许多fstream
对象和lambda,对于每个fstream
,都有一个lambda写入其中.因此,对fstream
对象的访问仅由lambda完成.我想为某些回调逻辑执行此操作.是否像我尝试过的那样使用lambda来实现更漂亮的方式?
- Why is this error raising up?
- My idea: I generate many
fstream
objects and lambdas in afor
loop, and for eachfstream
there is one lambda writing to it. So the access to thefstream
objects is only done by the lambdas. I want do this for some callback logic. Is there a more pretty way to this with lambdas like I tried?
推荐答案
发生此错误是因为您的lambda具有不可复制的捕获,从而使lambda本身不可复制. std::function
要求包装的对象是可复制构造的
The error happens because your lambda has non-copyable captures, making the lambda itself not copyable. std::function
requires that the wrapped object be copy-constructible.
如果您可以控制call_func
,请使其成为模板:
If you have control over call_func
, make it a template:
template<typename T>
void call_func(T&& func)
{
func();
}
int main()
{
std::fstream fs{"test.txt", std::fstream::out};
auto lam = [fs = std::move(fs)] { const_cast<std::fstream&>(fs).close(); };
call_func(lam);
}
以下是我对(2)中您的想法的看法.由于std::function
要求包装的对象是可复制构造的,因此我们可以制作不具有此限制的函数包装器:
Following is my take on your idea in (2). Since std::function
requires the wrapped object to be copy-constructible, we can make our own function wrapper that does not have this restriction:
#include <algorithm>
#include <fstream>
#include <iterator>
#include <utility>
#include <memory>
#include <sstream>
#include <vector>
template<typename T>
void call_func(T&& func) {
func();
}
// All functors have a common base, so we will be able to store them in a single container.
struct baseFunctor {
virtual void operator()()=0;
};
// The actual functor is as simple as it gets.
template<typename T>
class functor : public baseFunctor {
T f;
public:
template<typename U>
functor(U&& f)
: f(std::forward<U>(f))
{}
void operator()() override {
f();
}
};
// In C++17 you don't need this: functor's default constructor can already infer T.
template<typename T>
auto makeNewFunctor(T&& v) {
return std::unique_ptr<baseFunctor>(new functor<T>{std::forward<T>(v)});
}
int main() {
// We need to store pointers instead of values, for the virtual function mechanism to behave correctly.
std::vector<std::unique_ptr<baseFunctor>> functors;
// Generate 10 functors writing to 10 different file streams
std::generate_n(std::back_inserter(functors), 10, [](){
static int i=0;
std::ostringstream oss{"test"};
oss << ++i << ".txt";
std::fstream fs{oss.str(), std::fstream::out};
return makeNewFunctor([fs = std::move(fs)] () mutable { fs.close(); });
});
// Execute the functors
for (auto& functor : functors) {
call_func(*functor);
}
}
请注意,虚拟调用的开销是不可避免的:由于您需要将具有不同行为的函子存储在同一容器中,因此从本质上讲,您需要一种或多种方式的多态行为.因此,您可以手动实现此多态性,也可以使用virtual
.我更喜欢后者.
Note that the overhead from the virtual call is unavoidable: Since you need functors with different behavior stored in the same container, you essentially need polymorphic behavior one way or the other. So you either implement this polymorphism by hand, or use virtual
. I prefer the latter.
这篇关于通过带有移动捕获的lambda起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!