Как работает std :: asyn c: почему он так много раз вызывает копирование / перемещение? - PullRequest
1 голос
/ 27 мая 2020

Обычно заголовок

#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;
}

Результат:

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

Один ход - передать объект в асинхронный c, второй - передать его функции внутри asyn c, а что делает еще один ход?

1 Ответ

3 голосов
/ 28 мая 2020

Прежде всего, для передачи значения в async() перемещение не требуется, ссылка пересылки просто привязывается к исходному значению.

Затем, вместо того, чтобы угадывать, просто спросите саму программу, почему она делает копии / перемещаются.

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

Как мы видим:

  1. Первый ход происходит при создании invoker (std::thread::__make_invoker()) . Вызыватель - это своего рода упакованная задача, которая может быть позже выполнена как единое целое.

  2. Второй ход происходит, когда вызывающий объект упаковывается в объект async_state и сохраняется внутри future (__future_base::_S_make_async_state())

  3. Третий ход происходит при вызове лямбды (_State::_M_run())

Все это детали реализации и могут изменяться в зависимости от версии стандартной библиотеки.

...