Lets say
- I have a non-trivial class object
A
that defines a copy and move constructor - I move this object to a function which takes either
A
orA&&
Now in case of foo
's parameter type being A
, the move constructor is called (this is to be expected as A&&
is passed as an argument to the function-local A).
But in case of A&&
, the move constructor is not called, and in fact no constructor is called. Why?
I would have assumed that this happens because of copy elision, but it also happens with the -O0 flag set for gcc. I also thought that && basically just flags
an rvalue for overload resolution in the parameter list because inside the function body, it is treated as an lvalue. But this would mean that an lvalue A
would bind a moved A&&
and the move constructor should be called once again. What am I missing?
Compiled under x86-x64 gcc 11.2
#include <iostream>
#include <string>
#include <string.h>
struct A
{
char * str_;
A() {
std::cout << "normal constructor called\n";
str_ = new char[7];
sprintf(str_, "Hello!");
}
~A(){ delete[] str_; }
A(A& copy) {
std::cout << "copy constructor called\n";
str_ = strdup(copy.str_);
}
A(A&& moved) {
std::cout << "move constructor called\n";
str_ = moved.str_;
moved.str_ = nullptr;
}
};
void foo(A&& a)
{
std::cout << a.str_ << "\n";
}
int main()
{
A obj;
foo(std::move(obj));
}
CodePudding user response:
When foo
takes A&&
, you are binding to a r-value reference and not making any new A
objects that need construction.
This is because std::move
is basically just a cast to r-value reference.
When foo
takes A
, you are passing a r-value reference to A
as a means of constructing A
. Here, the move constructor is chosen as it takes A&&
as its argument.