Capture std::promise in a lambda C++14
std::function
can only be constructed from functors that are copyable. From [func.wrap.func.con]:
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.
std::promise
is non-copyable, so there's no way to stick a functor with this member into a std::function
. Period.
Given that you want your functor to actually take ownership of the promise, this doesn't leave you many options. Pretty much std::shared_ptr<std::promise>
. Any other option either doesn't work (e.g. std::unique_ptr<std::promise>
), leaves you with a dangling object (e.g. std::reference_wrapper<std::promise>
), or leaves you with memory-management issues (e.g. std::promise*
).
You could, however, use something other than std::function
. You can take a look at Yakk's task
idea here, as well as dyp's function_mo
here, both of which create movable flavors of std::function
.
It's trivial to roll your own polymorphic function class. This example fixes the argument and return types, but a little more work could templatise them if desired.
#include <iostream>
#include <functional>
#include <future>
#include <list>
// declare a non-polymorphic container for any function object that takes zero args and returns an int
// in addition, the contained function need not be copyable
class move_only_function
{
// define the concept of being callable while mutable
struct concept
{
concept() = default;
concept(concept&&) = default;
concept& operator=(concept&&) = default;
concept(const concept&) = delete;
concept& operator=(const concept&) = default;
virtual ~concept() = default;
virtual int call() = 0;
};
// model the concept for any given function object
template<class F>
struct model : concept
{
model(F&& f)
: _f(std::move(f))
{}
int call() override
{
return _f();
}
F _f;
};
public:
// provide a public interface
int operator()() // note: not const
{
return _ptr->call();
}
// provide a constructor taking any appropriate object
template<class FI>
move_only_function(FI&& f)
: _ptr(std::make_unique<model<FI>>(std::move(f)))
{}
private:
std::unique_ptr<concept> _ptr;
};
std::list<move_only_function> callbacks;
void addToCallbacks(move_only_function&& callback)
{
callbacks.push_back(std::move(callback));
}
int main()
{
std::promise<int> prom;
auto fut = prom.get_future();
// I have made the lambda mutable, so that the promise is not const, so that I can call the set_value
auto callback = [proms=std::move(prom)]() mutable { proms.set_value(5); return 5; };
// This now compiles
addToCallbacks(std::move(callback));
std::promise<int> prom2;
auto fut2 = prom2.get_future();
// this also compiles
addToCallbacks([proms = std::move(prom2)]() mutable { proms.set_value(6); return 6; });
for (auto& f : callbacks)
{
std::cout << "call returns " << f() << std::endl;
}
std::cout << "fut = " << fut.get() << std::endl;
std::cout << "fut2 = " << fut2.get() << std::endl;
return 0;
}
expected output:
call returns 5
call returns 6
fut = 5
fut2 = 6