seertaak seertaak - 6 months ago 34
C++ Question

How to create an std::function from a move-capturing lambda expression?

I'm trying to create an std::function from a move-capturing lambda expression. Note that I can create a move-capturing lambda expression without problems; it's only when I try to wrap it in an std::function that I get an error.

For example:

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);

I'll explain why I want to write something like this. I've written a UI library which, similar to jQuery or JavaFX, allows the user to handle mouse/keyboard events by passing std::functions to methods with names like on_mouse_down(), on_mouse_drag(), push_undo_action(), etc.

Obviously, the std::function I want to pass in should ideally use a move-capturing lambda expression, otherwise I need to resort to the ugly "release/acquire-in-lambda" idiom I was using when C++11 was the standard:

std::function<void()> baz = [q = pi.release()] {
std::unique_ptr<int> p{q};
*p = 5;
std::cout << *q << std::endl;

Note that calling baz twice would be an error in the above code. However, in my code, this closure is guaranteed to be called exactly once.

BTW, in my real code, I'm not passing an std::unique_ptr of int, but something more interesting.

Finally, I'm using Xcode6-Beta4 which uses the following version of 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


template<class F> function(F f);

template <class F, class A> function(allocator_arg_t, const A& a, F f);

Requires: F shall be CopyConstructible. f shall be Callable for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

§ [func.wrap.func.con]

Note that operator = is defined in terms of this constructor and swap, so the same restrictions apply:

template<class F> function& operator=(F&& f);

Effects: function(std::forward<F>(f)).swap(*this);

§ [func.wrap.func.con]

So to answer your question: Yes, it is possible to construct a std::function from a move-capturing lambda (since this only specifies how the lambda captures), but it is not possible to construct a std::function from a move-only type (e.g. a move-capturing lambda which move-captures something that is not copy constructible).