如何从捕获移动的lambda表达式中创建std :: function?

我正在尝试std::function从捕获移动的lambda表达式创建一个。请注意,我可以毫无问题地创建一个捕获移动的lambda表达式;只有当我尝试将其包装为时std::function,我才会收到错误消息。


例如:


auto pi = std::make_unique<int>(0);


// no problems here!

auto foo = [q = std::move(pi)] {

    *q = 5;

    std::cout << *q << std::endl;

};


// All of the attempts below yield:

// "Call to implicitly-deleted copy constructor of '<lambda...."


std::function<void()> bar = foo;

std::function<void()> bar{foo};

std::function<void()> bar{std::move(foo)};

std::function<void()> bar = std::move(foo);

std::function<void()> bar{std::forward<std::function<void()>>(foo)};

std::function<void()> bar = std::forward<std::function<void()>>(foo);

我将解释为什么我要写这样的东西。我写了一个UI库,类似于jQuery的或JavaFX的,允许用户通过传递给处理鼠标/键盘事件std::functions到方法有相似的名字on_mouse_down(),on_mouse_drag(),push_undo_action(),等。


显然,std::function我想传入的参数在理想情况下应使用捕获移动的lambda表达式,否则我需要诉诸在C ++ 11为标准时使用的难看的“ release / acquire-in-lambda”习惯用法:


std::function<void()> baz = [q = pi.release()] {

    std::unique_ptr<int> p{q};

    *p = 5;

    std::cout << *q << std::endl;

};

请注意,baz两次调用将是上述代码中的错误。但是,在我的代码中,此闭包被保证只被调用一次。


顺便说一句,在我的真实代码中,我没有传递std::unique_ptr<int>,而是更有趣的东西。


最后,我正在使用Xcode6-Beta4,它使用以下版本的clang:


Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)

Target: x86_64-apple-darwin13.3.0

Thread model: posix


慕田峪9158850
浏览 820回答 3
3回答

拉丁的传说

template<class F> function(F f);template <class F, class A> function(allocator_arg_t, const A& a, F f);要求: F应为CopyConstructible。f应该Callable用于参数类型ArgTypes和返回类型R。A的复制构造函数和析构函数不得抛出异常。§20.9.11.2.1[func.wrap.func.con]请注意,operator =是根据此构造函数和定义的swap,因此存在相同的限制:template<class F> function& operator=(F&& f);效果: function(std::forward<F>(f)).swap(*this);§20.9.11.2.1[func.wrap.func.con]因此,要回答你的问题:是的,这是可以构建一个std::function从一招捕获拉姆达(因为这仅指定拉姆达如何捕捉),但它是不是可以构建一个std::function从唯才是举型(如MOVE-捕获lambda,该lambda移动捕获无法复制构造的内容)。

慕妹3242003

由于std::function<?>必须对存储的可调用对象的副本构造函数进行类型擦除,因此不能从仅移动类型构造它。您的lambda因为它按值捕获仅移动类型,所以它是仅移动类型。所以...您无法解决您的问题。&nbsp; std::function无法存储您的lambda。至少不是直接。这是C ++,我们只是解决问题。template<class F>struct shared_function {&nbsp; std::shared_ptr<F> f;&nbsp; shared_function() = delete; // = default works, but I don't use it&nbsp; shared_function(F&& f_):f(std::make_shared<F>(std::move(f_))){}&nbsp; shared_function(shared_function const&)=default;&nbsp; shared_function(shared_function&&)=default;&nbsp; shared_function& operator=(shared_function const&)=default;&nbsp; shared_function& operator=(shared_function&&)=default;&nbsp; template<class...As>&nbsp; auto operator()(As&&...as) const {&nbsp; &nbsp; return (*f)(std::forward<As>(as)...);&nbsp; }};template<class F>shared_function< std::decay_t<F> > make_shared_function( F&& f ) {&nbsp; return { std::forward<F>(f) };}至此,我们可以解决您的问题。auto pi = std::make_unique<int>(0);auto foo = [q = std::move(pi)] {&nbsp; *q = 5;&nbsp; std::cout << *q << std::endl;};std::function< void() > test = make_shared_function( std::move(foo) );test(); // prints 5a的语义与shared_function其他函数略有不同,因为它的副本std::function与原始函数具有相同的状态(包括变成a时)。我们还可以编写仅移动一次触发函数:template<class Sig>struct fire_once;template<class T>struct emplace_as {};template<class R, class...Args>struct fire_once<R(Args...)> {&nbsp; // can be default ctored and moved:&nbsp; fire_once() = default;&nbsp; fire_once(fire_once&&)=default;&nbsp; fire_once& operator=(fire_once&&)=default;&nbsp; // implicitly create from a type that can be compatibly invoked&nbsp; // and isn't a fire_once itself&nbsp; template<class F,&nbsp; &nbsp; std::enable_if_t<!std::is_same<std::decay_t<F>, fire_once>{}, int> =0,&nbsp; &nbsp; std::enable_if_t<&nbsp; &nbsp; &nbsp; std::is_convertible<std::result_of_t<std::decay_t<F>&(Args...)>, R>{}&nbsp; &nbsp; &nbsp; || std::is_same<R, void>{},&nbsp; &nbsp; &nbsp; int&nbsp; &nbsp; > =0&nbsp; >&nbsp; fire_once( F&& f ):&nbsp; &nbsp; fire_once( emplace_as<std::decay_t<F>>{}, std::forward<F>(f) )&nbsp; {}&nbsp; // emplacement construct using the emplace_as tag type:&nbsp; template<class F, class...FArgs>&nbsp; fire_once( emplace_as<F>, FArgs&&...fargs ) {&nbsp; &nbsp; rebind<F>(std::forward<FArgs>(fargs)...);&nbsp; }&nbsp; // invoke in the case where R is not void:&nbsp; template<class R2=R,&nbsp; &nbsp; std::enable_if_t<!std::is_same<R2, void>{}, int> = 0&nbsp; >&nbsp; R2 operator()(Args...args)&&{&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; R2 ret = invoke( ptr.get(), std::forward<Args>(args)... );&nbsp; &nbsp; &nbsp; clear();&nbsp; &nbsp; &nbsp; return ret;&nbsp; &nbsp; } catch(...) {&nbsp; &nbsp; &nbsp; clear();&nbsp; &nbsp; &nbsp; throw;&nbsp; &nbsp; }&nbsp; }&nbsp; // invoke in the case where R is void:&nbsp; template<class R2=R,&nbsp; &nbsp; std::enable_if_t<std::is_same<R2, void>{}, int> = 0&nbsp; >&nbsp; R2 operator()(Args...args)&&{&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; invoke( ptr.get(), std::forward<Args>(args)... );&nbsp; &nbsp; &nbsp; clear();&nbsp; &nbsp; } catch(...) {&nbsp; &nbsp; &nbsp; clear();&nbsp; &nbsp; &nbsp; throw;&nbsp; &nbsp; }&nbsp; }&nbsp; // empty the fire_once:&nbsp; void clear() {&nbsp; &nbsp; invoke = nullptr;&nbsp; &nbsp; ptr.reset();&nbsp; }&nbsp; // test if it is non-empty:&nbsp; explicit operator bool()const{return (bool)ptr;}&nbsp; // change what the fire_once contains:&nbsp; template<class F, class...FArgs>&nbsp; void rebind( FArgs&&... fargs ) {&nbsp; &nbsp; clear();&nbsp; &nbsp; auto pf = std::make_unique<F>(std::forward<FArgs>(fargs)...);&nbsp; &nbsp; invoke = +[](void* pf, Args...args)->R {&nbsp; &nbsp; &nbsp; return (*(F*)pf)(std::forward<Args>(args)...);&nbsp; &nbsp; };&nbsp; &nbsp; ptr = {&nbsp; &nbsp; &nbsp; pf.release(),&nbsp; &nbsp; &nbsp; [](void* pf){&nbsp; &nbsp; &nbsp; &nbsp; delete (F*)(pf);&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; };&nbsp; }private:&nbsp; // storage.&nbsp; A unique pointer with deleter&nbsp; // and an invoker function pointer:&nbsp; std::unique_ptr<void, void(*)(void*)> ptr{nullptr, +[](void*){}};&nbsp; void(*invoke)(void*, Args...) = nullptr;};通过emplace_as<T>标签甚至支持不可移动的类型。请注意,您必须()在右值上下文中进行评估(即,在后面std::move),因为沉默的破坏性行为()似乎是不礼貌的。此实现不使用SBO,因为如果这样做,它将要求存储的类型是可移动的,并且(对我来说)引导将需要更多工作。

心有法竹

这是一个更简单的解决方案:&nbsp; &nbsp;auto pi = std::make_unique<int>(0);&nbsp; &nbsp;auto ppi = std::make_shared<std::unique_ptr<int>>(std::move(pi));&nbsp; &nbsp;std::function<void()> bar = [ppi] {&nbsp; &nbsp; &nbsp; &nbsp; **ppi = 5;&nbsp; &nbsp; &nbsp; &nbsp; std::cout << **ppi << std::endl;&nbsp; &nbsp;};
打开App,查看更多内容
随时随地看视频慕课网APP