Consider this program that essentially creates std::thread
that calls the function func()
with arg
as argument:
#include <thread>
#include <iostream>
struct foo {
foo() = default;
foo(const foo&) { std::cout << "copy ctor" << std::endl; }
foo(foo&&) noexcept { std::cout << "move ctor" << std::endl; }
};
void func(foo){}
int main() {
foo arg;
std::thread th(func, arg);
th.join();
}
My output is
copy ctor
move ctor
move ctor
As far as I understand arg
is copied internally in the thread object and then passed to func()
as an rvalue (moved). So, I expect one copy construction and one move construction.
Why is there a second move construction?
std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.
Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&.
Waiting for threads to finish To wait for a thread use the std::thread::join() function. This function makes the current thread wait until the thread identified by *this has finished executing.
You pass argument to func
by value which should constitute the second move. Apparently std::thread
stores it internally one more time before calling func
, which AFAIK is absolutely legal in terms of the Standard.
So, I expect one copy construction and one move construction.
The standard doesn't actually say that. An implementation is allowed to do extra internal move constructions.
Doing so is potentially less efficient though. This was https://gcc.gnu.org/PR69724 and has been fixed for the upcoming GCC 10 release.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With