Let's take the following C++ sample:
#include <iostream>
struct X
{
std::string s;
X() : s("X") { }
X(const X& other) : s{other.s} { std::cout << "cpy-ctor\n"; }
X(X&& o): s{o.s} { o.s = ""; std::cout << "move-ctor\n"; }
X& operator=(const X& other) {
std::cout << "cpy-assigned\n";
s = other.s;
return *this;
}
X& operator=(X&& other) {
if (this != &other) {
s = other.s;
other.s = "";
}
std::cout << "move assigned\n";
return *this;
}
};
X f(X x) {
std::cout << "f: ";
return x;
}
X g() {
std::cout << "g: ";
X x;
return x;
}
int main() {
X x;
X y;
x = f(X());
y = g();
}
If I compile it with gcc 4.8.2, I have the following result:
f: move-ctor
move assigned
g: move assigned
I do not understand why copy-constructor is not called when I am calling the g function.
I am just trying to understand when the copy or the move constructors are called.
Although you are correct to identify that there is, logically, a copy/move of the local variable x from inside g() when it is returned, a useful feature of C++ is that it can elide (i.e. skip) this operation in many cases, even when the copy/move would have side-effects. This is one of those cases. When performed, this is known as the named return value optimisation.
It's arguably less useful than it was before we had move semantics, but it's still nice to have. Indeed, C++17 made it mandatory in some (select) cases.
In C++ all the expressions are:
lvalue
prvalue
xvalue
Constructing an object
- lvalue
Y x{};
Y y{x}; // copy constructor, x is an lvalue
- prvalue - RVO
With RVO, which is enabled by gcc by default. This elides using the copy constructor and instead the object is constructed once.
X g()
{
X x {};
x.value = 10;
return x;
}
X y {g()}; // X constructor get's called only once to create "y". Also
// y is passed a a reference to g() where y.value = 10.
// No copy/move constructor for optimization "as if" rule
- prvalue - No RVO
Without RVO, in this case it depends. If the move constructors are explicitly or implicitly deleted, then it will call the copy constructor
Copy constructor
struct X { X(const X&) {}}; // implicitly deletes move constructor
// and move assignment, see rule of 5
X g()
{
return X{}; // returns a prvalue
}
X y {g()}; // prvalue gets converted to xvalue,
// "temporary materialization", where the xvalue has an
// identity where members can be copied from. The xvalue
// binds to lvalue reference, the one from copy constructor
// argument
Move constructor
X { X(X&&) {}}; // explicitly declared move constructor
X g()
{
return X{}; // returns a prvalue
}
X y {g()}; // prvalue gets converted to xvalue,
// "temporary materialization", where the xvalue has an
// identity where members can be moved from. The xvalue
// binds to rvalue reference, the one from move constructor
// argument
- xvalue
X x {};
X y {std::move(x)}; // std::move returns an xvalue, where if move
// constructor is declared will call it, other wise
// copy constructor, similar to explained above for
// prvalue.
Copy/move assignment
- lvalue
X x{};
X y{};
x = y; // call copy assignment operator since y is an lvalue.
- prvalue
If the move assignments are explicitly or implicitly deleted, then it will call the copy assignment operator.
Copy assignment
struct X{ X& operator=(const X&); } // implicilty deletes move
// constructor and move assignment,
// see rule of 5
X g()
{
return X{}; // returns a prvalue
}
x = g(); // prvalue gets converted to xvalue,
// "temporary materialization", where the xvalue has an identity
// where members can be copied from. The xvalue binds to lvalue
// reference, the one from copy assignment operator argument
Move assignment
struct X{ X& operator=(X&&); } // explicitly declared move assignment operator
X g()
{
return X{}; // returns a prvalue
}
x = g(); // prvalue gets converted to xvalue,
// "temporary materialization", where the xvalue has an identity
// where members can be moved from. The xvalue binds to rvalue
// reference, the one from move assignment operator argument
- xvalue
X x {};
X y {};
x = std::move(x); // std::move returns an xvalue, where if move
// assignment is declared will call it, other
// wise copy assignment, similar to explained
// above for prvalue.
A copy constructor is called when you instantiate an object using another object of the same type.
For example:
X x;
X y(x);
The last line in your code assigns a value returned from a function to an already constructed object. This is done via move assignment.
Related
Consider the following code, Entity object is non-movable. I know that std::move(Obj) just cast the Obj to a rvalue reference Obj. And I also know that rvalue reference variable is still a lvalue object. But I still confusing why statement auto temp = std::move(a) can call copy constructor, statement a = std::move(b); can call copy assignment operator. Since, std::move(a) is a rvalue reference, why it can still call lvalue constructor.
#include <iostream>
template<typename T>
void my_swap(T& a, T& b) {
auto temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
class Entity{
int _id;
public:
Entity(int id) :_id(id) {std::cout << "construtor\n" << std::endl;}
Entity(const Entity& other) {
_id = other._id;
std::cout << "copy constructor\n" << std::endl;
}
Entity& operator= (const Entity& other) {
if (&other == this) return *this;
this->_id = other._id;
std::cout << "copy assignment operator\n";
return *this;
}
};
int main() {
Entity e1 = Entity(10);
Entity e2 = Entity(20);
my_swap(e1,e2);
return 0;
}
Entity object is non-movable
No. Even it doesn't have move constructor/assignment-operator, it has copy constructor/assignment-operator taking lvalue-reference to const. std::move(a) and std::move(b) are rvalue (xvalue) expressions and they could be bound to lvalue-reference to const.
You might also check std::is_move_constructible:
Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible.
If you want to make your Entity non-movable, then add the move-constructor and move-assignment operator as deleted:
Entity(Entity&& other) = delete;
Entity& operator= (Entity&& other) = delete;
Considering the following code, Entity object is non-movable.
No, it isn't. Entity is movable.
But I still confusing why statement auto temp = std::move(a)
Lvalue reference to const can be bound to an rvalue. The copy constructor accepts an lvalue reference to const parameter. That parameter can be bound to the std::move(a) rvalue argument.
And I also know that rvalue reference variable is still a lvalue object.
Not quite. References are not objects. An id-expression that names anything, including an rvalue reference varaible, is an lvalue.
Entity is movable. Does is because there exists default move constructor/assignment-operator.
It is movable because it has a copy constructor/assignment operator, and it doesn't have deleted move constructor/assignment operator.
For the following code, I know that because I implement a copy constructor the default move is blocked by the compiler, what I don't understand is why the compiler choose the copy constructor, and not issuing an error?
Can a rvalue reference be the parameter for a reference function if no rvalue ref function exists?
If not, what do happens here?
#include <iostream>
using namespace std;
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
};
int main()
{
A a;
A b = std::move(a);
return 1;
}
The output is:
constructor
copy constructor
Thanks
The reason is that rvalue expressions can be bound to function parameters that are const lvalue references.
Also note that const rvalue expressions cannot be bound to rvalue references:
class A{
public:
A(){printf("constructor\n");}
A(const A& a){printf("copy constructor");}
A(A&&) = default; // <-- has move ctor
};
int main()
{
const A a; // <-- const!
A b = std::move(a); // <-- copy not move!!!
return 1;
}
The code above still calls the copy constructor, even though there is a move constructor.
std::move will take your named value and turn it into an rvalue expression, nothing more.
And, just like for any other rvalue, if it can't be moved, it'll be copied.
An rvalue of type T can be bound to a const T& just fine.
The Standard provides an example regarding to the a move constructor. There is what it says:
A non-template constructor for class X is a move constructor if its
first parameter is of type X&&, const X&&, volatile X&&, or const
volatile X&&, and either there are no other parameters or else all
other parameters have default arguments (8.3.6).
I was trying to run some an experiments with an example the Stadard provides:
#include <iostream>
#include <limits>
struct Y {
Y(){ std::cout << "Y()" << std::endl; };
Y(const Y&){ std::cout << "Y(const Y&)" << std::endl; };
Y(Y&&){ std::cout << "Y(const Y&&)" << std::endl; };
};
Y f(int)
{
return Y();
}
Y d(f(1)); // calls Y(Y&&)
Y e = d; // calls Y(const Y&)
int main(){ }
DEMO
But instead copy constructor was called. Why that?
The copy-constructor is invoked by the line:
Y e = d;
This cannot be a move operation , because d is an lvalue. This is why you see the copy constructor call in your output.
For the line Y d(f(1)), d is moved from the rvalue f(1) (which was in turn moved from Y()), however copy elision means that the output of both of these move-constructors may be suppressed.
This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 8 years ago.
Class A defines all copy/move constructor/assignment as follows:
struct A
{
std::string s;
A() : s("test") { }
A(const A& other) : s(other.s) { std::cout << "Copy constructor\n";}
A(A&& other) : s(std::move(other.s)) { std::cout << "Move constructor\n";}
A& operator= (const A& other) { std::cout << "Copy assignment\n"; s = other.s; return *this;}
A& operator= (A&& other) { std::cout << "Move assignment\n"; s = std::move(other.s); return *this;}
};
And the followings are functions returning an object of type A:
A f(A a) { return a; }
A g() { return A(); }
The main() function is this:
int main()
{
A a1 = f(A()); // Move-construct
A a2 = std::move(a1); // Move-construct
A a3 (std::move(a2)); // Move-construct
A a4 (a1); // Copy-construct
A a5 = a4; // Copy-construct
a5 = f(A()); // Move constructor + Move assignment
a5 = a4; // Copy assignment
a5 = g(); // Move assignment
A a6 = g(); // None!! Member-wise assignment (?)
}
Can anybody tell me, why on earth none of the constructors and assignment operators is called for a6? Which part of C++11's documentation describes this behavior?
This is called copy elision and is described in C++ standard, section 12.8 pt 31.
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects. In such cases, the implementation treats the
source and target of the omitted copy/move operation as simply two
different ways of referring to the same object (...)
The circumstances are described as well. One of which being:
when a temporary class object that has not been bound to a reference
would be copied/moved to a class object with the same cv-unqualified
type, the copy/move operation can be omitted by constructing the
temporary object directly into the target of the omitted copy/move
As g() returns a temporary anonymous object (not bound to a reference), is is constructed directly into the target a6. In your example, the default constructor of g's return statement is used.
With http://en.wikipedia.org/wiki/Return_value_optimization in A a6 = g();, the code within g creates a6 by calling one of the A constructors somewhere in the middle of the body of g.
According to what I know it is not valid to bind a lvalue into a rvalue reference.
And secondly, a lvalue expression is recognizable by the fact it can be prefix by adress-of operator, (&)
I'm a little bit in trouble if this two sentences arecorrect with the following codes :
#include<iostream>
struct Foo
{
Foo(Foo&& other)
{
std::cout << "move ctor called";
}
Foo(const Foo& other)
{
std::cout << "copy ctor called";
}
Foo(){}
};
Foo return_foo()
{
Foo f;
return f;
}
void main()
{
Foo f = return_foo(); // Move ctor is called, but return_foo() is a lvalue ??
std::cin.ignore();
}
Where I am wrong ?
return_foo() returns a prvalue (because it returns unnamed temporary object). Quote from §3.10/1, emphasis mine:
A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [
Example: The result of calling a function whose return type is not a
reference is a prvalue. The value of a literal such as 12, 7.3e5, or
true is also a prvalue. —end example ]
There is a special rule which allows returning a temporary as an rvalue, namely, the following are equivalent - the explicit "I don't need this anymore" version:
T foo()
{
T t(a, b, ...); // constructed somehow
/* ... */
return std::move(t);
}
int main()
{
T t = foo(); // we can move-construct this
}
... and the implicit version:
T foo()
{
T t(a, b, ...);
/* ... */
return t; // implicitly allow moving
}
All this happens after return-value optimization. This means that returning by value is actually pretty efficient in many situations.