How to create an std::function from a move-capturing lambda expression?
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
Requires:
F
shall beCopyConstructible
.f
shall beCallable
for argument typesArgTypes
and return typeR
. The copy constructor and destructor of A shall not throw exceptions.§20.9.11.2.1 [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);
§20.9.11.2.1 [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).
Move capture in lambda
Generalized lambda capture in C++14
In C++14 we will have the so called generalized lambda capture. This enables move capture. The following will be legal code in C++14:
using namespace std;
// a unique_ptr is move-only
auto u = make_unique<some_type>( some, parameters );
// move the unique_ptr into the lambda
go.run( [ u = move(u) ] { do_something_with( u ); } );
Also note if you need to move object from lambda to some other function you need to make lambda mutable
.
go.run( [ u = move(u) ] mutable { do_something_with( std::move(u) ); } );
The generalized lambda capture is much more general in the sense that captured variables can be initialized with anything like so:
auto lambda = [value = 0] mutable { return ++value; };
In C++11 this is not possible yet, but with some tricks that involve helper types. Fortunately, the Clang 3.4 compiler already implements this awesome feature. The compiler will be released December 2013 or January 2014, if the recent release pace will be kept.
UPDATE: The Clang 3.4 compiler was released on 6 Jan 2014 with the said feature.
A workaround for move capture
Here's an implementation of a helper function make_rref
which helps with artificial move capture
#include <cassert>
#include <memory>
#include <utility>
template <typename T>
struct rref_impl
{
rref_impl() = delete;
rref_impl( T && x ) : x{std::move(x)} {}
rref_impl( rref_impl & other )
: x{std::move(other.x)}, isCopied{true}
{
assert( other.isCopied == false );
}
rref_impl( rref_impl && other )
: x{std::move(other.x)}, isCopied{std::move(other.isCopied)}
{
}
rref_impl & operator=( rref_impl other ) = delete;
T && move()
{
return std::move(x);
}
private:
T x;
bool isCopied = false;
};
template<typename T> rref_impl<T> make_rref( T && x )
{
return rref_impl<T>{ std::move(x) };
}
And here's a test case for that function that ran successfully on my gcc 4.7.3.
int main()
{
std::unique_ptr<int> p{new int(0)};
auto rref = make_rref( std::move(p) );
auto lambda =
[rref]() mutable -> std::unique_ptr<int> { return rref.move(); };
assert( lambda() );
assert( !lambda() );
}
The drawback here is that lambda
is copyable and when copied the assertion in the copy constructor of rref_impl
fails leading to a runtime bug. The following might be a better and even more generic solution because the compiler will catch the error.
Emulating generalized lambda capture in C++11
Here's one more idea, on how to implement generalized lambda capture. The use of the function capture()
(whose implementation is found further down) is as follows:
#include <cassert>
#include <memory>
int main()
{
std::unique_ptr<int> p{new int(0)};
auto lambda = capture( std::move(p),
[]( std::unique_ptr<int> & p ) { return std::move(p); } );
assert( lambda() );
assert( !lambda() );
}
Here lambda
is a functor object (almost a real lambda) which has captured std::move(p)
as it is passed to capture()
. The second argument of capture
is a lambda which takes the captured variable as an argument. When lambda
is used as a function object, then all arguments that are passed to it will be forwarded to the internal lambda as arguments after the captured variable. (In our case there are no further arguments to be forwarded). Essentially, the same as in the previous solution happens. Here's how capture
is implemented:
#include <utility>
template <typename T, typename F>
class capture_impl
{
T x;
F f;
public:
capture_impl( T && x, F && f )
: x{std::forward<T>(x)}, f{std::forward<F>(f)}
{}
template <typename ...Ts> auto operator()( Ts&&...args )
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}
template <typename ...Ts> auto operator()( Ts&&...args ) const
-> decltype(f( x, std::forward<Ts>(args)... ))
{
return f( x, std::forward<Ts>(args)... );
}
};
template <typename T, typename F>
capture_impl<T,F> capture( T && x, F && f )
{
return capture_impl<T,F>(
std::forward<T>(x), std::forward<F>(f) );
}
This second solution is also cleaner, because it disables copying the lambda, if the captured type is not copyable. In the first solution that can only be checked at runtime with an assert()
.
Passing a lambda with moved capture to 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.
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);
}
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);
}
}
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.
Capturing lambda in std::function results in extra copies
AFAICT the problem is the const
of the foo()
argument. When you capture t
inside foo(const Test& t)
, then the type of that capture inside the lambda is also const
. Later when you forward the lambda, the lambda's move constructor will have no choice but copy, not move, the capture. You cannot move from const
.
After changing foo to foo(Test& t)
I get:
main()
Test: Constructor
foo()
Test: Copy Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
Alternative solution, mentioned in https://stackoverflow.com/a/31485150/85696, is to use capture in the form [t=t]
.
With move-capture and two other changes it is possible to eliminate this remaining copy constructor too:
- void foo(const Test& t) {
+ void foo(Test t) {
...
- queue([t](){
+ queue([t = std::move(t)](){
...
- foo(test1);
+ foo(std::move(test1));
main()
Test: Constructor
Test: Move Constructor
foo()
Test: Move Constructor
queue()
Test: Move Constructor
Test: Move Constructor
Test: Destructor
Test: Destructor
Test: Destructor
dequeue()
_foo()
t={a=20}
Test: Destructor
main() return
Test: Destructor
How to create an std::function from a move-capturing lambda expression?
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
Requires:
F
shall beCopyConstructible
.f
shall beCallable
for argument typesArgTypes
and return typeR
. The copy constructor and destructor of A shall not throw exceptions.§20.9.11.2.1 [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);
§20.9.11.2.1 [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).
Move capture of parameter in lambda
Move semantics only work on mutable objects. You will need to make your lambda mutable:
auto func_lambda = [b = std::move(b)]() mutable {
f(std::move(b));
};
However you can only call such lambda once. If you want to call it multiple time, you'll have to generate values or use std::exchange(b, B{})
lambda capture of std function
What am I doing wrong?
getAnd
returns a function object from a member function, which captures and accesses members.
You call that member function on a local variable, and return the resulting function object to the outside of the scope. The members pointed by the function object no longer exist and calling the function object results in undefined behaviour.
When I replace funcs with funcs_outside, everything works well.
funcs_outside
is a global object and you access it within its lifetime, so there is no problem.
how can I fix this?
You could for example capture a copy of the member instead:
return [funcs = this->funcs](const State &s)
lambda move capture for std::thread
template<class Function, class... Args, class Rep, class Period>
void Timer::setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args) {
this->clear = false;
std::thread t([this, f=std::move(f), sleep_duration, args = std::make_tuple(std::forward<Args>(args)...))]() {
while(true) {
if (this->clear) return;
std::this_thread::sleep_for(sleep_duration);
if(this->clear) return;
std::apply(f, args);
}
});
t.detach();
}
That should do correct capturing. Note that as the timer goes off more than once, you don't want to further-forward data into the f
.
But that still doesn't work because you didn't capture this
.
Really, you should do a proper cv/mutex and not leak threads.
class Timer {
std::condition_variable cv;
mutable std::mutex m;
bool clear = false;
std::thread active_thread;
void dispose_thread() {
if (active_thread.joinable()) {
stop();
active_thread.join();
}
clear = false; // safe, because all threads are dead
}
public:
template<class Function, class... Args, class Rep, class Period>
[[maybe_unused]] void setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args);
auto lock() const {
return std::unique_lock( m );
}
void stop() {
auto l = lock();
clear = true;
cv.notify_all();
}
~Timer() { dispose_thread(); }
};
template<class Function, class... Args, class Rep, class Period>
void Timer::setInterval(const std::chrono::duration<Rep, Period>& sleep_duration, Function&& f, Args&&... args) {
dispose_thread();
// capture everything explicitly; when messing with threads,
// best to understand state
active_thread = std::thread(
[this, f = std::move(f), sleep_duration, args = std::make_tuple(std::forward<Args>(args)...)]()
{
while(true) {
{
auto l = lock();
// return value here isn't useful, but the function is:
(void)cv.wait_for(l, sleep_duration, [&]{ return clear; });
if (clear)
return;
}
std::apply(f, args);
}
}
);
}
and .stop()
and .join()
in the destructor.
Live example.
This prevents you from leaking threads and outliving your timer object and doesn't make you wait for a timer interval to set a new interval and the like.
You should also set up sleep_until
time points, as if you want to do something every second and the f
function takes 0.1 seconds, this will actually repeat every 1.1 seconds.
If you want to maintain multiple worker threads in one Timer
object, I'd switch to using std::future
s in a vector, and maybe sweeping them to clean up ones that are finished at various intervals.
The idea of waiting for existing tasks to cleanup before starting a new tasks has value to me. If you don't like that, you could have the threads report back that they are ready to clean up, and clean them up lazily later. But this also requires multiple clear
variables, so the threads that are delayed don't get the wrong value when the next thread is ready to go.
Moving a lambda: once you've move-captured a move-only type, how can the lambda be used?
You can move the lambda, that's fine. That's not what your problem is though, you're trying to instantiate a std::function
with a noncopyable lambda. And the:
template< class F >
function( F f );
constructor of function
does:
5) Initializes the target with a copy of
f
.
This is because std::function
:
satisfies the requirements of CopyConstructible and CopyAssignable.
Since function
has to be copyable, everything you put into it must also be copyable. And a move-only lambda does not meet that requirement.
Lambda capture-by-value while transfering ownership
A lambda is not a std::function
.
You are right that c++14 allows moving something into a lambda, and even move said lambda afterwards.
A std::function
on the other hand requires the callable to be Copy Constructable and Copy Assignable.
From cppreference
Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.
The stored callable object is called the target of std::function. If a std::function contains no target, it is called empty. Invoking the target of an empty std::function results in std::bad_function_call exception being thrown.
std::function satisfies the requirements of CopyConstructible and CopyAssignable.
Some ideas for a possible solution could be:
Create your own function wrapper (or use a library) that supports move-only callables.
Wrap you
std::unique_pre
in astd::shared_ptr
and capture that.
The first option seems much more robust, but just to show the idea for a quick dirty work-around.
#include <memory>
#include <functional>
int main() {
auto moveonly = std::make_unique<int>(5);
auto wrapped = std::make_shared<std::unique_ptr<int>>(std::move(moveonly));
auto lam = [wrapped]() {
return *(*wrapped);
};
std::function<int()> f = lam;
}
Related Topics
Is It Safe to Delete a Null Pointer
To_String Is Not a Member of Std, Says G++ (Mingw)
Initializer_List and Move Semantics
How to Redirect Cin and Cout to Files
How to Add Additional Libraries to Visual Studio Project
Difference Between Char A[] = String; and Char *P = String;
Why Do C and C++ Compilers Allow Array Lengths in Function Signatures When They'Re Never Enforced
How to Use Boost in Visual Studio 2010
Why How to Not Push_Back a Unique_Ptr into a Vector
Why Isn't It Legal to Convert "Pointer to Pointer to Non-Const" to a "Pointer to Pointer to Const"
Recommended Way to Initialize Srand
Variable Length Array (Vla) in C++ Compilers
Copy a File in a Sane, Safe and Efficient Way
Is #Pragma Once a Safe Include Guard