What does moving a lambda actually do?

Hi,

This is a non-trivial question so please give it the merit it deserves. It has to do with the following code where I have defined a temporary lambda object which is forwarded (and thus "moved") when it arrives at the call site. My question is: how is this different from an exact copy of the function call that would not use std::forward on the passed lambda (as func)?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	using namespace std;

	template<typename Functor, typename ... Args>
	void call(Functor&& func, Args&&... args)
	{
              // this next prints 1 always!! - thus func is an rvalue reference
	      cout << is_rvalue_reference<decltype(func)>::value << endl;

	      std::forward<Functor>(func)(std::forward<Args>(args)...);
	}

	void useLambdas() {
	     int i = 99;

	     call([](int i) {  cout << "lambda2(int): " << i << endl; }, 100);
	}


Thanks
Juan
> What does moving a lambda actually do?

Whether a closure type is trivially copyable or not is left to the implementation to decide.

In practice, there is no difference between copying and moving an object of a closure type.

If there is no capture by value, the closure type is typically trivially copyable and trivially moveable.

If there is capture by value, the move constructors of the closure type would copy the captured-by-value objects. The closure type would be trivially copyable and trivially moveable if and only if all captured by value objects are of trivially copyable and trivially moveable types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <type_traits>

struct A
{
    A() = default ;
    ~A() noexcept = default ;
    A( const A& ) { std::cout << "A::copy construct\n" ; }
    A( A&& ) noexcept  { std::cout << "A::move construct\n" ; }
    A& operator= ( const A& ) = default ;
    A& operator= ( A&& ) noexcept = default ;
};

template < typename T > void traits()
{
    std::cout << std::boolalpha
              << "trivially_copy_constructible? "
              << std::is_trivially_copy_constructible<T>::value
              << "\ntrivially_move_constructible? "
              << std::is_trivially_move_constructible<T>::value << '\n' ;
}

int main()
{
    A a ;

    {
        std::cout << "capture by value\n" ;
        const auto closure = [ a = std::move(a) ] {} ; // A::move construct
        traits< decltype(closure) >() ; // not trivially_copy_constructible, not trivially_move_constructible
        
        // copy constructor of the closure copies the object captured by value
        const auto copy_constructed = std::move(closure) ; // A::copy construct

        // move constructor of the closure copies (does not move) the object captured by value
        const auto move_constructed = std::move(closure) ; // A::copy construct
    }

    {
        std::cout << "\ncapture by reference\n" ;
        const auto closure = [&a] {} ; // capture by reference
        traits< decltype(closure) >() ; // trivially_copy_constructible, trivially_move_constructible
    }
}

http://coliru.stacked-crooked.com/a/b11d1d8d55dedf3d
http://rextester.com/DQEOV44701
As JLBorges stated: There is no particular difference between copy and move, except that in case of move there is an option that the rvalue (right of =) can be modified, but nothing forces the object to actually do so.
Thanks for the good answers to both of you!!


Juan
Topic archived. No new replies allowed.