I am trying to understand value categories in C++ a bit better, and I encountered some code that got me a bit confused.
I looked at some questions such as Accept move-only parameter by value or rvalue reference or How is it possible to pass move-only types (e.g. std::unique_ptr) by value?, but I did not find any comment explaining what happens under the hood or why it is possible to pass as arguments rvalues of move-only types by value. Take the example below:
class Foo
{
Foo(std::thread t) : mT(std::move(t))
private:
std::thread mT;
};
int main()
{
std::thread t;
Foo f(t); // doesn't compile as std::thread is not copyable
Foo f{std::thread()}; // compiles just fine using an rvalue
}
Calling Foo f(t);
would call the non-existent copy-constructor of std::thread
. However, what happens under the hood for the same not to happen when constructing a Foo
object with the rvalue std::thread()
? Why is the rvalue
not copied?
Why is the rvalue not copied?
The expression std::thread()
is the initialisation of the parameter t
. It is the same as why there isn't a copy when you write std::thread t;
.
In the implementation of Foo::Foo
, the object t
is passed to std::move
, which casts it to an rvalue reference, so that it matches std::thread::thread(std::thread&&)
, i.e. thread's move constructor.