通过带有移动捕获的lambda起作用 [英] Passing a lambda with moved capture to function

查看:60
本文介绍了通过带有移动捕获的lambda起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近为一个难以找到的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.

我现在有两个问题:

  1. 为什么会出现此错误?
  2. 我的想法:我在for循环中生成了许多fstream对象和lambda,对于每个fstream,都有一个lambda写入其中.因此,对fstream对象的访问仅由lambda完成.我想为某些回调逻辑执行此操作.是否像我尝试过的那样使用lambda来实现更漂亮的方式?
  1. Why is this error raising up?
  2. My idea: I generate many fstream objects and lambdas in a for loop, and for each fstream there is one lambda writing to it. So the access to the fstream 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆