6
votes

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?

2
Ah, a dupe in fact. Shame it can't be closed as such due to having no answers (because n.m. wrote their answer in the comments section for some reason)Lightness Races in Orbit

2 Answers

3
votes

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.

1
votes

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.