Why move constructor is called instead of copy constructor - c++

I'm trying to understand a move constructor,
usually a copy constructor is called when objects are copied
that what happen when I do not provide a move constructor,
but when I add a move constructor it is called instead of my copy constructor,
here is my code:
#include <iostream>
#include <vector>
using namespace std;
struct A
{
A()
{
cout << "A's constructor" << endl;
}
A(const A& rhs)
{
cout << "A's copy constructor" << endl;
}
A(A&& rhs)
{
cout << "A's move constructor" << endl;
}
};
int main() {
vector<A> v;
cout << "==> push_back A():";
v.push_back(A());
cout << "==> push_back A():" << endl;
v.push_back(A());
return 0;
}
does the compiler try to optimize my code and choose the better method ?

Basically, yes.
However, this is not about compiler optimisations as much as it is about how the language itself has been optimised.
Half of the point of move semantics is to allow efficient use of temporaries. That's why temporaries bind to rvalue refs and it's how the entire move semantics thing works. When you don't have a temporary but you wish to move something anyway, you write std::move to obtain an rvalue and trigger this same behaviour.
There is no point copying those temporaries when they are about to be destroyed anyway.
In concert with copy/move elision, this feature will result in much less redundant computing.
Note, however, that your move constructor is not terribly useful — if you ever wanted to make it actually do something (like, um, move stuff) you'd have to remove that const.

rvalue references are better candidates to bind temporaries than const lvalue references. It is not about optimizing (as in compiler optimizations), it is about following standard. The actual optimization would be to not call copy constructor at all.

Related

Understanding the reasoning between copy/move constructors and operators

I am trying to get the grasp of rvalue references and move semantics with a simple self-made example but I can't understand a specific part. I have created the following class:
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
I tried the following experiments to see if I can predict how the constructors/operators are going to be called:
A a1(1) - The default constructor is going to be called.
PREDICTED.
A a2 = a1 - The copy constructor is going to be called. PREDICTED.
a1 = a2 - The copy assignment operator is going to be called.
PREDICTED.
Now, I created a simple function that just returns an A object.
A helper() {
return A(1);
}
A a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. The move
constructor is not going to be called due to RVO. PREDICTED.
a3 = helper() - The default constructor is going to be called in
order to create the object that the helper returns. Then, the move
assignment operator is going to be called. PREDICTED.
Now comes the part I don't understand. I created another function that is completely pointless. It takes an A object by value and it just returns it.
A helper_alt(A a) {
return a;
}
A a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then the move
constructor. PREDICTED.
a4 = helper_alt(a1) - This will call the copy constructor, to
actually copy the object a1 in the function and then I thought that
the move assignment operator is going to be called BUT as I saw,
first, the move constructor is called and then the move assignment
operator is called. HAVE NO IDEA.
Please, if any of what I said is wrong or you feel I might have not understood something, feel free to correct me.
My actual question: In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?
Congratulations, you found a core issue of C++!
There are still a lot of discussions around the behavior you see with your example code.
There are suggestions like:
A&& helper_alt(A a) {
std::cout << ".." << std::endl;
return std::move(a);
}
This will do what you want, simply use the move assignment but emits a warning from g++ "warning: reference to local variable 'a' returned", even if the variable goes immediately out of scope.
Already other people found that problem and this is already made a c++ standard language core issue
Interestingly the issue was already found in 2010 but not solved until now...
To give you an answer to your question "In the last case, why is the move constructor being called and then the move assignment operator, instead of just the move assignment operator?" is, that also C++ committee does not have an answer until now. To be precise, there is a proposed solution and this one is accepted but until now not part of the language.
From: Comment Status
Amend paragraph 34 to explicitly exclude function parameters from copy elision. Amend paragraph 35 to include function parameters as eligible for move-construction.
consider the below example. I have compiled the sample code using -fno-elide-constructors flag to prevent RVO optimizations:
g++ -fno-elide-constructors -o test test.cpp
#include<iostream>
using namespace std;
class A {
public:
A(int a) {
cout << "Def constructor" << endl;
}
A(const A& var) {
cout << "Copy constructor" << endl;
}
A(A&& var) {
cout << "Move constructor" << endl;
}
A& operator=(const A& var) {
cout << "Copy Assignment" << endl;
return *this;
}
A& operator=(A&& var) {
cout << "Move Assignment" << endl;
return *this;
}
};
A a_global(1);
A helper_alt(A a) {
return a;
}
A helper_a_local(A a) {
A x(1);
return x;
}
A helper_a_global(A a) {
return a_global;
}
int main(){
A a1(1);
A a4(4);
std::cout << "================= helper_alt(a1) ==================" << std::endl;
a4 = helper_alt(a1);
std::cout << "=============== helper_a_local() ================" << std::endl;
a4 = helper_a_local(a1);
std::cout << "=============== helper_a_global() ================" << std::endl;
a4 = helper_a_global(a1);
return 0;
}
This will result in the below output:
Def constructor
Def constructor
Def constructor
================= helper_alt(a1) ==================
Copy constructor
Move constructor
Move Assignment
=============== helper_a_local() ================
Copy constructor
Def constructor
Move constructor
Move Assignment
=============== helper_a_global() ================
Copy constructor
Copy constructor
Move Assignment
In simple words, C++ constructs a new temporary object (rvalue) when the return type is not a reference, which results in calling Move or Copy constructor depending on the value category and the lifetime of the returned object.
Anyway, I think the logic behind calling the constructor is that you are not working with reference, and returned identity should be construed first, either by copy or move constructor, depending on the returned value category or lifetime of the return object. As another example:
A helper_move_vs_copy(A a) {
// Call the Copy Constructor
A b = a;
// Call the Move Constructor, Due to the end of 'a' lifetime
return a;
}
int main(){
A a1(1);
A a2(4);
std::cout << "=============== helper_move_vs_copy() ================" << std::endl;
helper_move_vs_copy(a1);
return 0;
}
which outputs:
Def constructor
Def constructor
=============== helper_move_vs_copy() ================
Copy constructor
Copy constructor
Move constructor
From cppreference:
an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
At last, it is the job of RVO to decrease unnecessary moves and copies by optimization of the code, which can even result in an optimized binary for basic programmers!

Copy Elision in visual 2019

I was trying to test a small code to check if my compiler (under Visual Studio 2019) does copy elision, as it is no more optional for some cases under C++17.
So I tried the code below :
#include <iostream>
#include <vector>
using namespace std;
struct myStruct
{
myStruct() { cout << "default ctor" << endl; }
myStruct(const myStruct& str) { cout << "copy ctor" << endl; }
myStruct& operator=(myStruct other) { cout << "Copy assignement" << endl; return *this; }
myStruct(myStruct&& str) noexcept { cout << "move ctor" << endl; }
myStruct& operator=(myStruct&& other) { cout << "move assignement" << endl; return *this; }
~myStruct() { cout << "deleted" << endl; }
};
myStruct get()
{
myStruct s;
return s;
}
int main()
{
vector<myStruct> vect;
vect.reserve(10);
cout << "Creating The Vector" << endl;
vect.push_back(get());
vect.push_back(get());
cout << "Cretion End" << endl;
return 0;
}
So as far as I read, calling the function get() will trigger the RVO so I will get only ctor calls. But when I run the program I get (I added <<<< in front of lines that are related to copy after calling get() function):
Creating The Vector
default ctor
move ctor
deleted
move ctor <<<<
deleted <<<<
default ctor
move ctor
deleted
move ctor <<<<
deleted <<<<
Cretion End
deleted
deleted
When tried with gcc I get :
Creating The Vector
default ctor
move ctor
deleted
default ctor
move ctor
deleted
Cretion End
deleted
deleted
So seems that Microsoft still didn't implement the Copy elision, or is there wrong in my code so I missed the real charm of Copy elision?
Thank you in advance
None of the copies in this case are required to be elided by any (currently existing) version of C++. The standard lays down several copies: the copy from s to the return value object of get, and the copy from the reference parameter to push_back into the vector's internal object. The latter copy cannot be elided, and the former copy's elision is not guaranteed to happen at all.
If you're talking about C++17's guaranteed elision, this only applies to the use of a prvalue to initialize an object (of that type). That never happens here (outside of the temporary parameter passed to call push_back, but that's normal for any version of C++), so it doesn't apply.
Here's a simple test: if the source object of the hypothetical copy has a variable name, then elision if applicable is not mandatory.

C++ copy constructor double call on member initialization

Consider the below code, where a composing class with another class as its member is being instantiated:
class CopyAble {
private:
int mem1;
public:
CopyAble(int n1) : mem1(n1) {
cout << "Inside the CopyAble constructor" << endl;
}
CopyAble(const CopyAble& obj) {
cout << "Inside the CopyAble copy constructor" << endl;
this->mem1 = obj.mem1;
return *this;
}
CopyAble& operator=(const CopyAble& obj) {
cout << "Inside the CopyAble assignment constructor" << endl;
this->mem1 = obj.mem1;
}
~CopyAble() {};
};
class CopyAbleComposer {
private:
CopyAble memObj;
public:
CopyAbleComposer(CopyAble m1) : memObj(m1) {
cout << "Composing the composer" << endl;
}
~CopyAbleComposer() {}
};
int main()
{
CopyAble ca(10);
CopyAbleComposer cac(ca);
return 0;
}
When I run this, I get the output:
Inside the CopyAble constructor
Inside the CopyAble copy constructor
Inside the CopyAble copy constructor
Composing the composer
Which means that the CopyAble copy constructor is being run twice - once when the CopyAble object is passed into the CopyAbleComposer constructor, and again when the initializer memObj(m1) runs.
Is this an idiomatic use of the copy constructor? It seems very inefficient that the copy constructor runs twice when we try to initialize a member object with a passed-in object of the same type, and it seems like a trap a lot of C++ programmers can easily fall into without realizing it.
EDIT: I don't think this is a duplicate of the question regarding passing a reference into the copy constructor. Here, we are being forced to pass a reference into a regular constructor to avoid duplicate object creation, my question was that is this generally known that class constructors in C++ should have objects passed in by reference to avoid this kind of duplicate copy?
You should accept CopyAble by reference at CopyAbleComposer(CopyAble m1), otherwise a copy constructor will be called to construct an argument. You should also mark it as explicit to avoid accidental invocations:
explicit CopyAbleComposer(const CopyAble & m1)
Pass-by-value and the associated copying is a pretty widely known property of C++. Actually, in the past C++ was criticized for this gratuitious copying, which happened silently, was hard to avoid and could lead to decreased performance. This is humorously mentioned e.g. here:
You accidentally create a dozen instances of yourself and shoot them all in the foot. Providing emergency medical assistance is impossible since you can't tell which are bitwise copies and which are just pointing at others and saying, "That's me, over there."
C++98
When any function/method is declared to receive an argument by value, this sort of copying happens. It doesn't matter if it's a constructor, a "stand-alone" function or a method. To avoid this, use a const reference:
CopyAbleComposer(const CopyAble& m1) : memObj(m1)
{
...
}
Note: even if you rearrange your code as below, one copy always remains. This has been a major deficiency in C++ for a long time.
CopyAbleComposer cac(CopyAble(10)); // initializing mem1 by a temporary object
C++11
C++11 introduced move semantics, which replaces the additional copy by a "move" operation, which is supposed to be more efficient than copy: in the common case where an object allocates memory dynamically, "move" only reassigns some pointers, while "copy" allocates and deallocates memory.
To benefit from optimization offered by move semantics, you should undo the "optimization" you maybe did for C++98, and pass arguments by value. In addition, when initializing the mem1 member, you should invoke the move constructor:
CopyAbleComposer(CopyAble m1) : memObj(std::move(m1)) {
cout << "Composing the composer" << endl;
}
Finally, you should implement the move constructor:
CopyAble(CopyAble&& obj) {
cout << "Inside the CopyAble move constructor" << endl;
this->mem1 = obj.mem1;
}
Then you should see that the "copy" message doesn't appear, and is replaced by the "move" message.
See this question for more details.
Note: In all these examples, the CopyAble objects are assumed to be much more complex, with copy and move constructors doing non-trivial work (typically, resource management). In modern C++, resource management is considered a separate concern, in the context of separation of concerns. That is, any class that needs a non-default copy or move constructor, should be as small as possible. This is also called the Rule of Zero.

Cannot understand why perfect forwarding is not working

I am trying to understand how perfect forwarding works but I cannot understand why the copy constructor is called in the code below
#include <utility>
#include <iostream>
using std::cout;
using std::endl;
class Something {
public:
Something() = default;
Something(__attribute__((unused)) const Something& other) {
cout << "Copy constructor called" << endl;
}
Something(__attribute__((unused)) Something&& other) {
cout << "Move constructor called" << endl;
}
void print() {
cout << "Something::print() called" << endl;
}
};
void function_1(Something&& one) {
cout << "version two called" << endl;
Something inner{one};
inner.print();
}
void function_1(const Something& one) {
Something inner(one);
inner.print();
}
template <typename... T>
void test_function(T&&... ts) {
function_1(std::forward<T>(ts)...);
}
int main() {
const Something some1 {Something()};
test_function(some1);
test_function(Something());
return 0;
}
This produces the following output
Copy constructor called
Something::print() called
version two called
Copy constructor called
Something::print() called
Changing the code to include std::move in the rvalue reference works but I did not expect to need it. When a reference is an rvalue reference the correct constructor should be called automatically right? The correct reference is resolved but the wrong constructor is being called. Any help would be greatly appreciated!
An rvalue reference binds to rvalues. It is not itself an rvalue, for it has a name.
But anything with a name at point of use is an lvalue by default, even rvalue references. Your code could use Something&& one three times, and if the first use implicitly moves you would be screwed.
Instead, it is an lvalue at point of use (by default), and it binds to an rvalue.
When you want to signal you no longer require its state to persist, std::move it.
Perfect forwarding can be used to write both of your function_1s by putting a std::forward<Blah>(blah) at the point where you'd want to move from blah if it was an rvalue reference.
Now the above is full of lies, for there are xvalues prvalues lvalues etc -- the standard is more complex. The use of a variable in return statements can turn a named value into an rvalue, for example. But the basic rule of thumb is worth knowing: it has a name, it is an lvalue (except if explicitly casted, or expiring).
This code will call the copy ctor, not the move ctor.
void function_1(Something&& one) {
cout << "version two called" << endl;
Something inner{one};
inner.print();
}
This code calls the move ctor.
void function_1(Something&& one) {
cout << "version two called" << endl;
Something inner{std::move(one)};
inner.print();
}
The expression one is technically an l-value. It refers to an rvalue-reference. But to actually get the rvalue-reference you have to use std::move. Generally anything that has a name is an l-value. Unnamed temporaries, like your Something() expression in main():
test_function(Something());
can be rvalue's and can invoke a move without using std::move.

RVO, move semantics and the struggle towards optimal code

If I get it correctly, move semantics allows to move and reuse resources from temporary, unnamed objects. RVO, albeit preceding move semantics goes further and "steals" the entire object to avoid the extra constructor call and assignment/copy function.
This seems a bit counter intuitive to me, wouldn't it be that much faster, simple and user obvious if the called constructor uses directly the address of the final lvalue target to directly emplace data where the user needs it?
I mean, "create this object in this location" seems a bit more intuitive than "create this object somewhere, then copy it to its right location".
Yes it is "a bit counter intuitive". With copy elision enabled all side effects of the constructor are elided, too.
#include <iostream>
struct X {
X() { std::cout << "Construct" << std::endl; }
X(X&&) { std::cout << "Move" << std::endl; }
~X() { std::cout << "Destruct" << std::endl; };
};
X f() { return X(); }
int main()
{
X x(f());
return 0;
}
Copy elision: g++ -std=c++11 src-test/main.cc
Construct
Destruct
No copy elision: g++ -std=c++11 -fno-elide-constructors src-test/main.cc
Construct
Move
Destruct
Move
Destruct
Destruct
The compiler, knowing the hardware the program/library is build for, is able to apply (optional) copy elision.
The C++ language, itself, is not aware of hardware specific return mechanisms. Hence it is not possible to construct at a certain address in this context.