DoehJohn
DoehJohn

Reputation: 233

How std::async works: why it invokes copy/move so many times?

Basically the title

#include <iostream>
#include <future>

class A
{
    public:
    A () {};
    A (const A& a) {std::cout<<"A copy\n";};
    A (A&& a) noexcept {std::cout<<"A move\n";};
    ~A ()  {std::cout<<"A dest\n";};
    void fun () { std::cout<<"fun\n";}
};

int main()
{
    A a;
    std::future<void> f = std::async([](A a) { a.fun(); }, std::move(a));
    f.wait();
    return 0;
}

Result is

A move
A move
A dest
A move
fun
A dest
A dest
A dest

One move is to pass an object to an async, one to pass it to the function inside async, but one more move is doing what?

Upvotes: 2

Views: 353

Answers (1)

rustyx
rustyx

Reputation: 85351

First of all, no move is needed to pass the value to async(), the forwarding reference simply binds to the original value.

Then, instead of guessing, just ask the program itself why it's making the copies/moves.

$ g++ --version; g++ -g ./a.cpp -pthread
g++ (Debian 8.3.0-6) 8.3.0

$ gdb ./a.out
GNU gdb (Debian 8.2.1-2+b3) 8.2.1
Reading symbols from ./a.out...done.
(gdb) b 9
Breakpoint 1 at 0x5b78: file ./a.cpp, line 9.
(gdb) r
Starting program: ./a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, A::A (this=0x7ffffffedbdf, a=...) at ./a.cpp:9
9           A (A&& a) noexcept {std::cout<<"A move\n";};
(gdb) bt
#0  A::A (this=0x7ffffffedbdf, a=...) at ./a.cpp:9
#1  0x00000000080070ba in std::_Head_base<1ul, A, true>::_Head_base<A> (this=0x7ffffffedbdf, __h=...) at /usr/include/c++/8/tuple:87
#2  0x0000000008007088 in std::_Tuple_impl<1ul, A>::_Tuple_impl<A> (this=0x7ffffffedbdf, __head=...) at /usr/include/c++/8/tuple:366
#3  0x000000000800286c in std::_Tuple_impl<0, main()::<lambda(A)>, A>::_Tuple_impl<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (this=0x7ffffffedbdf, __head=...)
    at /usr/include/c++/8/tuple:218
#4  0x0000000008002831 in std::tuple<main()::<lambda(A)>, A>::tuple<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (this=0x7ffffffedbdf, __a1=..., __a2=...)
    at /usr/include/c++/8/tuple:972
#5  0x00000000080028d1 in std::thread::__make_invoker<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (__callable=..., __args#0=...) at /usr/include/c++/8/thread:268
#6  0x0000000008002600 in std::async<main()::<lambda(A)>, A>(std::launch, <lambda(A)> &&, A &&) (__policy=(std::launch::async | std::launch::deferred), __fn=..., __args#0=...)
    at /usr/include/c++/8/future:1719
#7  0x00000000080024d3 in std::async<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (__fn=..., __args#0=...) at /usr/include/c++/8/future:1749
#8  0x000000000800241c in main () at ./a.cpp:17
(gdb) c
Continuing.
A move

Breakpoint 1, A::A (this=0x8022ec8, a=...) at ./a.cpp:9
9           A (A&& a) noexcept {std::cout<<"A move\n";};
(gdb) bt
#0  A::A (this=0x8022ec8, a=...) at ./a.cpp:9
#1  0x00000000080070ba in std::_Head_base<1ul, A, true>::_Head_base<A> (this=0x8022ec8, __h=...) at /usr/include/c++/8/tuple:87
#2  0x0000000008007056 in std::_Tuple_impl<1ul, A>::_Tuple_impl (this=0x8022ec8, __in=...) at /usr/include/c++/8/tuple:373
#3  0x0000000008002ab0 in std::_Tuple_impl<0, main()::<lambda(A)>, A>::_Tuple_impl(std::_Tuple_impl<0, main()::<lambda(A)>, A> &&) (this=0x8022ec8, __in=...)
    at /usr/include/c++/8/tuple:227
#4  0x00000000080027e9 in std::tuple<main()::<lambda(A)>, A>::tuple(std::tuple<main()::<lambda(A)>, A> &&) (this=0x8022ec8) at /usr/include/c++/8/tuple:987
#5  0x0000000008003be3 in std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >::_Invoker(std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x8022ec8)
    at /usr/include/c++/8/thread:231
#6  0x0000000008003da5 in std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>::_Async_state_impl(std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x8022e90, __fn=...) at /usr/include/c++/8/future:1662
#7  0x0000000008003a61 in __gnu_cxx::new_allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >::construct<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> *, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x7ffffffeda2f,
    __p=0x8022e90, __args#0=...) at /usr/include/c++/8/ext/new_allocator.h:136
#8  0x0000000008003816 in std::allocator_traits<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > >::construct<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::allocator_traits<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > >::allocator_type &, std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> *, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (__a=..., __p=0x8022e90,
    __args#0=...) at /usr/include/c++/8/bits/alloc_traits.h:475
#9  0x000000000800344f in std::_Sp_counted_ptr_inplace<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x8022e80, __a=...) at /usr/include/c++/8/bits/shared_ptr_base.h:545
#10 0x000000000800306f in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> *&, std::_Sp_alloc_shared_tag<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (
    this=0x7ffffffedb78, __p=@0x7ffffffedb70: 0x0, __a=...) at /usr/include/c++/8/bits/shared_ptr_base.h:677
#11 0x0000000008002f4c in std::__shared_ptr<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::_Sp_alloc_shared_tag<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x7ffffffedb70, __tag=...) at /usr/include/c++/8/bits/shared_ptr_base.h:1342
#12 0x0000000008002ea1 in std::shared_ptr<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >::shared_ptr<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::_Sp_alloc_shared_tag<std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (this=0x7ffffffedb70, __tag=...) at /usr/include/c++/8/bits/shared_ptr.h:359
#13 0x0000000008002cfd in std::allocate_shared<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(const std::allocator<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> > &, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (__a=...,
    __args#0=...) at /usr/include/c++/8/bits/shared_ptr.h:706
#14 0x0000000008002b49 in std::make_shared<std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > &&) (__args#0=...) at /usr/include/c++/8/bits/shared_ptr.h:722
#15 0x00000000080029c4 in std::__future_base::_S_make_async_state<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> > >(std::thread::_Invoker<std::tuple<main()::<lambda(A)>,--Type <RET> for more, q to quit, c to continue without paging--c
 A> > &&) (__fn=...) at /usr/include/c++/8/future:1705
#16 0x0000000008002613 in std::async<main()::<lambda(A)>, A>(std::launch, <lambda(A)> &&, A &&) (__policy=(std::launch::async | std::launch::deferred), __fn=..., __args#0=...) at /usr/include/c++/8/future:1719
#17 0x00000000080024d3 in std::async<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (__fn=..., __args#0=...) at /usr/include/c++/8/future:1749
#18 0x000000000800241c in main () at ./a.cpp:17
(gdb) c
Continuing.
A move
[New Thread 0x7fffff250700 (LWP 141)]
A dest
[Switching to Thread 0x7fffff250700 (LWP 141)]

Thread 2 "a.out" hit Breakpoint 1, A::A (this=0x7fffff24f98f, a=...) at ./a.cpp:9
9           A (A&& a) noexcept {std::cout<<"A move\n";};
(gdb) bt
#0  A::A (this=0x7fffff24f98f, a=...) at ./a.cpp:9
#1  0x000000000800291c in std::__invoke_impl<void, main()::<lambda(A)>, A>(std::__invoke_other, <lambda(A)> &&, A &&) (__f=..., __args#0=...)
    at /usr/include/c++/8/bits/invoke.h:60
#2  0x0000000008002525 in std::__invoke<main()::<lambda(A)>, A>(<lambda(A)> &&, A &&) (__fn=..., __args#0=...) at /usr/include/c++/8/bits/invoke.h:95
#3  0x00000000080046bb in std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >::_M_invoke<0, 1>(std::_Index_tuple<0, 1>) (this=0x8022ec8) at /usr/include/c++/8/thread:244
#4  0x0000000008004642 in std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >::operator()(void) (this=0x8022ec8) at /usr/include/c++/8/thread:253
#5  0x0000000008004413 in std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>::operator()(void) const (this=0x7fffff24fdd0) at /usr/include/c++/8/future:1362
#6  0x000000000800413d in std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>(), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void> >::_M_invoke(const std::_Any_data &) (__functor=...) at /usr/include/c++/8/bits/std_function.h:283
#7  0x0000000008006067 in std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const (this=0x7fffff24fdd0)
    at /usr/include/c++/8/bits/std_function.h:687
#8  0x000000000800599b in std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (this=0x8022e90, __f=0x7fffff24fdd0, __did_set=0x7fffff24fd47) at /usr/include/c++/8/future:561
#9  0x0000000008006db3 in std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__f=
    @0x7fffff24fd60: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>()> *, bool *)) 0x8005974 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __t=@0x7fffff24fd58: 0x8022e90, __args#0=@0x7fffff24fd50: 0x7fffff24fdd0, __args#1=@0x7fffff24fd48: 0x7fffff24fd47)
    at /usr/include/c++/8/bits/invoke.h:73
#10 0x0000000008006633 in std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__fn=
    @0x7fffff24fd60: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>()> *, bool *)) 0x8005974 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args#0=@0x7fffff24fd58: 0x8022e90, __args#1=@0x7fffff24fd50: 0x7fffff24fdd0, __args#2=@0x7fffff24fd48: 0x7fffff24fd47)
    at /usr/include/c++/8/bits/invoke.h:95
#11 0x0000000008005e30 in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const (this=0x7fffff24fce0) at /usr/include/c++/8/mutex:672
#12 0x0000000008005e5b in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#2}::operator()() const (this=0x0) at /usr/include/c++/8/mutex:677
#13 0x0000000008005e6c in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_bas--Type <RET> for more, q to quit, c to continue without paging--c
e::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#2}::_FUN() () at /usr/include/c++/8/mutex:677
#14 0x00007fffff45e997 in __pthread_once_slow (once_control=0x8022ea8, init_routine=0x7fffff6dae20 <__once_proxy>) at pthread_once.c:116
#15 0x00000000080023bd in __gthread_once (__once=0x8022ea8, __func=0x7fffff6dae20 <__once_proxy>) at /usr/include/x86_64-linux-gnu/c++/8/bits/gthr-default.h:699
#16 0x0000000008005f05 in std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (__once=..., __f=@0x7fffff24fd60: (void (std::__future_base::_State_baseV2::*)(std::__future_base::_State_baseV2 * const, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>()> *, bool *)) 0x8005974 <std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*)>, __args#0=@0x7fffff24fd58: 0x8022e90, __args#1=@0x7fffff24fd50: 0x7fffff24fdd0, __args#2=@0x7fffff24fd48: 0x7fffff24fd47) at /usr/include/c++/8/mutex:684
#17 0x000000000800577c in std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) (this=0x8022e90, __res=..., __ignore_failure=false) at /usr/include/c++/8/future:401
#18 0x0000000008003c49 in std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>::<lambda()>::operator()(void) const (this=0x8022e90) at /usr/include/c++/8/future:1667
#19 0x000000000800431d in std::__invoke_impl<void, std::__future_base::_Async_state_impl<_BoundFn, _Res>::_Async_state_impl(_BoundFn&&) [with _BoundFn = std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >; _Res = void]::<lambda()> >(std::__invoke_other, std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>::<lambda()> &&) (__f=...) at /usr/include/c++/8/bits/invoke.h:60
#20 0x000000000800401e in std::__invoke<std::__future_base::_Async_state_impl<_BoundFn, _Res>::_Async_state_impl(_BoundFn&&) [with _BoundFn = std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >; _Res = void]::<lambda()> >(std::__future_base::_Async_state_impl<std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >, void>::<lambda()> &&) (__fn=...) at /usr/include/c++/8/bits/invoke.h:95
#21 0x0000000008004d80 in std::thread::_Invoker<std::tuple<std::__future_base::_Async_state_impl<_BoundFn, _Res>::_Async_state_impl(_BoundFn&&) [with _BoundFn = std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >; _Res = void]::<lambda()> > >::_M_invoke<0>(std::_Index_tuple<0>) (this=0x8022f08) at /usr/include/c++/8/thread:244
#22 0x0000000008004cd6 in std::thread::_Invoker<std::tuple<std::__future_base::_Async_state_impl<_BoundFn, _Res>::_Async_state_impl(_BoundFn&&) [with _BoundFn = std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >; _Res = void]::<lambda()> > >::operator()(void) (this=0x8022f08) at /usr/include/c++/8/thread:253
#23 0x00000000080048b0 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<std::__future_base::_Async_state_impl<_BoundFn, _Res>::_Async_state_impl(_BoundFn&&) [with _BoundFn = std::thread::_Invoker<std::tuple<main()::<lambda(A)>, A> >; _Res = void]::<lambda()> > > >::_M_run(void) (this=0x8022f00) at /usr/include/c++/8/thread:196
#24 0x00007fffff6dbb2f in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#25 0x00007fffff456fa3 in start_thread (arg=<optimized out>) at pthread_create.c:486
#26 0x00007fffff3794cf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
(gdb) c
Continuing.
A move
fun
A dest
A dest
[Thread 0x7fffff250700 (LWP 141) exited]
A dest
[Inferior 1 (process 137) exited normally]
(gdb)

As we can see:

  1. The first move happens when making an invoker (std::thread::__make_invoker()). An invoker is a sort of a packaged task that can be executed as a unit later.

  2. The second move happens when packaging the invoker into an async_state object and storing it inside a future (__future_base::_S_make_async_state())

  3. The third move takes place when invoking the lambda (_State::_M_run())

All of this is implementation detail and subject to change between versions of the standard library.

Upvotes: 4

Related Questions