将带有移动捕获的lambda传递给函数

Jul*_*anH 10 c++ lambda c++14

我最近在努力寻找一个很难找到的bug.我试图将lambda传递给一个获取std::function对象的函数.lambda正在捕获一个不可复制的对象.

我想通了,显然一些副本必须在所有通行证之间发生.我来到这个结果是因为我总是以error: use of deleted function错误结束.

以下是产生此错误的代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

我通过在std::fstream对象中加载对象来解决这个问题std::shared_ptr.这工作正常,但我认为可能有更性感的方式来做到这一点.

我现在有两个问题:

  1. 为什么这个错误会增加?
  2. 我的想法:我fstreamfor循环中生成了许多对象和lambda ,并且每个fstream都有一个lambda写入它.因此,fstream只能通过lambdas 来访问对象.我想为一些回调逻辑做这个.有没有像我尝试过的lambda那样更漂亮的方式?

Cás*_*nan 12

发生错误是因为你的lambda具有不可复制的捕获,使得lambda本身不可复制.std::function 要求包装的对象是可复制构造的.

如果您有控制权call_func,请将其设为模板:

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);
}
Run Code Online (Sandbox Code Playgroud)

以下是我在(2)中对你的想法的看法.由于std::function要求包装对象是可复制构造的,我们可以创建自己的函数包装器,它没有这个限制:

#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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,虚拟调用的开销是不可避免的:由于您需要将具有不同行为的仿函数存储在同一容器中,因此您基本上需要以某种方式进行多态行为.所以你要么手工实现这个多态,要么使用virtual.我更喜欢后者.