C++11 Move constructor optimization - c++

I'm currently trying to get a hang of move constructor.
I came upon the following (Compiled using g++ d.cpp --std=c++11 -O3)
class A {
string _x;
public:
A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x; }
A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x; }
A foo() {
cout << "foo: " << _x << "\n";
return A("foo");
}
};
int main()
{
A a;
A b = a;
b.foo();
}
I expect this to output:
default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo
However the output is:
default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo
Why isn't the A b = a line optimized to use the move constructor? The a object is never used afterwards, so it would be safe to optimize the code to use it instead of the copy constructor.
I know I could force the move contructor to be invoked with std::move(), but I'd prefer this to happen automatically in cases like this one.

Why isn't the A b = a line optimized to use the move constructor?
What you can do in copy constructor and move constructor could be totally different. The compiler cannot guarantee that the results of the two constructors are identical. Implementing this kind of optimization has the potential of changing the behavior of your program, which breaks the as-if rule.
You need to use std::move to cast a to A&&:
#include <utility>
int main()
{
A a("a");
A b = std::move(a);
b.foo();
}
A correct implementation of the move constructor should be:
A(A&& other)
: _x(std::move(other._x))
{}
After the line A b = std::move(a);, a should be "empty". In this case, a._x will be empty. as pointed by #TonyD in the comments, a._str could be in an unspecified but valid state (move constructor of std:string). You should use a with caution after this line.

A b = a; always invokes the copy constructor, no matter if it could invoke the move constructor. Additionally the lifetime of the object a continues after the assignment, even it is not used anymore.
If you want to use the move constructor, you have to make it explicit:
A b = std::move(a);
Note that this can be dangerous, as a is still accessible after the move. If you accidentally use it later, there may be undefined behavior.
Think about why it should happen automatically. In the example you gave, there is no need, as you can as well use a instead of b. In many cases where it would make more sense move constructor/assignment would be used automatically, e.g. A a; a = foo();.

Why isn't the A b = a line optimized to use the move constructor?
Because that would change the observable behavior of the program. The compiler is not permitted to freely change the observable behavior of the program (§1.9/1), except under very specific circumstances (§12.8/31). This is not one of those circumstances. Remove the side effects from your constructors, and the compiler may optimize them away. Of course, if you remove the side effects, then you won't notice if the compiler optimizes the constructor calls away (unless you examine the assembly or binary output), but that's the whole point.

Related

construction and destruction of parameterized constructor argument?

Here, i am getting different out on different compiler, why is that ?
On msvc compiler, there i'm getting extra destructor statement ?
Why i'm getting this behaviour ?
Am i missing something ?
i had looked many question on stackoverflow, but i can't find anything related to my problem ?
i also tried to look for duplicate, but didn't find one.
class A {
public:
A()
{
std::cout << "A::constructor" << "\n";
}
~A()
{
std::cout << "A::Destructor" << "\n";
}
int x = 0;
int y = 0;
};
class B {
public:
A member_var_1;
int member_var_2;
B()
{
std::cout << "B::constructor" << '\n';
}
B(A a, int b)
{
member_var_1 = a;
member_var_2 = b;
std::cout << "B(A, int)::constructor " << '\n';
}
~B()
{
std::cout << "B::destructor" << '\n';
}
};
int main()
{
B v1 {A(), 5};
}
GCC output:
A::consturctor // parameterized constructor first argument constructor
A::consturctor // construction of B's class member (member_var_1)
B(A, int)::consturcotr // B class parameterized constructor
A::Destructor // Destruction of argument of parameterized constructor
B::destructor // object goes out of scope, so B destructor called
A::Destructor // B's Destructor called member's destructor
MSVC output:
A::consturctor
A::consturctor
B(A, int)::consturcotr
A::Destructor
A::Destructor // what is it destroying? if i define a "class A" copy constructor, then i don't get this output.
B::destructor
A::Destructor
Since you're using C++17 and there is mandatory copy elision from C++17(&onwards), the extra destructor call must not be there.
A msvc bug has been reported as:
MSVC produces extra destructor call even with mandatory copy elision in C++17
Note that if you were to use C++11 or C++14, then it was possible to get an extra destructor call because prior to c++17 there was no mandatory copy elision and the parameter a could've been created using the copy/move constructor which means that you'll get the fourth destructor call as expected. You can confirm this by using the -fno-elide-constructors flag with other compilers. See Demo that has a contrived example of this.
B v1 {A(), 5};
In MSCV compiler, first, the construction of temporary A() is happening then it is elided to to "a" argument of parameterized constructor. that's why the extra destruction is happening for this temporary but it shouldn't be here because it is done implicitly.
So it is a bug in MSCV.

Unexpected behaviour with constructors

Simple program:
#include <iostream>
using namespace::std;
class X {
public:
X() {
cout << "Default Constructor called\n";
i = 0;
}
X(int i) {
cout << "Parameterized Constructor called\n";
this->i = i;
}
X(const X& x) {
cout << "Copy Constructor called\n";
i = x.getI();
}
~X() {
cout << "Destructor called\n";
}
int getI() const {
return i;
}
X func() {
cout << "Entered func\n";
X x(2);
return x;
}
private:
int i;
};
int main() {
X x1;
X x2 = x1.func();
cout << "Returned from func\n";
}
It outputs the following:
Default Constructor called
Entered func
Parameterized Constructor called
Copy Constructor called
Destructor called
Returned from func
Destructor called
Destructor called
My issue is that after the 'Returned from func' is printed, no constructor is called when creating the instance x2. I was actually expecting a copy constructor to be called when instantiating x2 as it would have been if we did something like X x2 = x1;
I initially incorrectly assumed that the reason was RVO, so I keep my original answer below.
What your program prints is expected. Note that you print "Returned from function" after you call it and assign the returned value to x2. So the order of prints is correct: it first instantiates x, hence parametrized ctor call, then it copies it to x2, as it exits from the function, then it calls the destructor of x, which is no longer needed, then it actually exits from the function, and only then it reaches your print statement.
Old answer follows
What you observe is Return Value Optimization.
Return value optimization, or simply RVO, is a compiler optimization
technique that involves eliminating the temporary object created to
hold a function's return value.1 In C++, it is particularly notable
for being allowed to change the observable behaviour of the resulting
program.[2]
In general, the C++ standard allows a compiler to perform any
optimization, provided the resulting executable exhibits the same
observable behaviour as if (i.e. pretending) all the requirements of
the standard have been fulfilled. This is commonly referred to as the
"as-if rule".[3] The term return value optimization refers to a
special clause in the C++ standard which goes even further than the
"as-if" rule: an implementation may omit a copy operation resulting
from a return statement, even if the copy constructor has side
effects.[4]
See this wikipedia article, it has an example that is very similar to yours

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.

move and copy semantics with the std::vector

How does one control which constructor/assignment operator is being used to insert elements into the std::vector class? I tried to do it by deleteing the constructor/assignment I wanted to avoid using as follows
#include<iostream>
#include<vector>
using namespace std;
class copyer{
double d;
public:
//ban moving
copyer(copyer&& c) = delete;
copyer& operator=(copyer&& c) = delete;
//copy construction
copyer(const copyer& c){
cout << "Copy constructor!" << endl;
d = c.d;
}
copyer& copy(const copyer& c){
cout << "Copy constructor!" << endl;
d = c.d;
return *this;
}
//Constructor
copyer(double s) : d(s) { }
double fn(){return d;}
};
class mover{
double d;
public:
//ban copying
mover(const mover& c) = delete;
mover& operator=(const mover& c) = delete;
//move construction
mover(mover&& c){
cout << "Move constructor!" << endl;
d = c.d;
}
mover& copy(mover&& c){
cout << "Move constructor!" << endl;
d = c.d;
return *this;
}
//Constructor
mover(double s) : d(s) { }
double fn(){return d;}
};
template<class P> class ConstrTests{
double d;
size_t N;
std::vector<P> object;
public:
ConstrTests(double s, size_t n) : d(s) , N(n) {
object.reserve(N);
for(int i = 0; i<N; i++){
object.push_back(P((double) i*d));
}
}
void test(){
int i = 0;
while(i<N){
cout << "Testing " <<i+1 << "th object: " << object.at(i).fn();
i++;
}
}
};
When I compile and run
size_t N = 10;
double d = 4.0;
ConstrTests<mover> Test1 = ConstrTests<mover>(d,N);
Test1.test();
I have no problems, but if instead I try
size_t N = 10;
double d = 4.0;
ConstrTests<copyer> Test1 = ConstrTests<copyer>(d,N);
Test1.test();
I get an error at compilation stating I'm trying to use a deleted move constructor.
If you remove these lines from copyer
//ban moving
copyer(copyer&& c) = delete;
copyer& operator=(copyer&& c) = delete;
then the code compiles fine and works as expected, that is, std::vector<copyer> uses the copy-constructor and std::vector<mover> uses the move-constructor.
You had declared copyer's move constructor and defined it as deleted. It means that copyer::copyer(copyer&& c) participates in overload resolution but if selected the code is ill formed. The call object.push_back(P((double) i*d)); triggers this call.
Why removing the lines above fixed the issue?
In old C++98 if we don't declare a copy-constructor the compiler declares and implement one for us. In C++11 this rules has changed a little bit. The compiler will not implicitly define a copy-constructor if a move constructor is user declared. (There's more on this subject but for this discussion this is enough.) Analogously, if we don't declare a move-constructor the compiler will implicitly define one for us unless we declare (for instance) a copy-constructor.
Now, after having removed the lines above, copyer will have a user declared copy-constructor buy not a move-constructor. Then, neither you nor the compiler would have declared a move-constructor. In that case, object.push_back(P((double) i*d)); will trigger a call to the copy-constructor. Notice that this is exaclty what would have happened if we compiled with a C++98 compliant compiler. In this case backward compatibility holds and old code doesn't break.
The version which is called, is determined by overload resolution. If you call push_back with a prvalue or a xvalue it'll use the move constructor, assuming your move constructor is noexcept, if it is not, it'll still use the copy constructor, if the copy constructor is deleted, you're forcing the vector to use your throwing move constructor, in which case the vector can no longer provide the strong exception guarantee. (Ie. If an exception happens from your move constructor, you've broken the content of the vector).
The general idea is, that whenever you do a push_back, reallocation may be needed, if this is the case, all elements will have to be transfered to the new memory block, using either a move or a copy constructor. Using the move constructor is generally nice, if we're sure it won't throw anything, because in the case it does, we've may already have moved some objects from the original vector, which is now broken. And moving them back is not an alternative as that may throw as well. This is why you loose the strong exception guarantee.
So in general, please do declare your move constructor noexcept, if you'd like it to be called over the copy constructor. (In the above example, if the copy constructor throws, you can just deallocate the new memory, and rethrow the exception, and the vector is still as before the push_back call).
So in general, you control which is called, based upon which constructors are available, and upon the type of the argument (lvalue vs prvalue/xvalue). Assuming your constructors are in place, you'll likely control it, by calling push_back, using std::move on it's argument, this effectively converts the argument to an xvalue, if possible.
Note; whatever I said about push_back, generally applies to insert as well.
Note2: the guarantee I'm talking about is, that whenever push_back is called, we're guaranteed that if an exception happens, the call will not have any effect, this is known as the strong exception guarantee, and it cannot be provided if all you got is a throwing move constructor.

can a C++ function return an object with a constructor and a destructor

I'm trying to establish whether it is safe for a C++ function to return an object that has a constructor and a destructor. My understanding of the standard is that it ought to be possible, but my tests with simple examples show that it can be problematic. For example the following program:
#include <iostream>
using namespace std;
struct My
{ My() { cout << "My constructor " << endl; }
~My() { cout << "My destructor " << endl; }
};
My function() { My my; cout << "My function" << endl; return my; }
int main()
{ My my = function();
return 0;
}
gives the output:
My constructor
My function
My destructor
My destructor
when compiled on MSVC++, but when compiled with gcc gives the following output:
My constructor
My function
My destructor
Is this a case of "undefined behavior", or is one of the compilers not behaving in a standard way? If the latter, which ? The gcc output is closer to what I would have expected.
To date, I have been designing my classes on the assumption that for each constructor call there will be at most one destructor call, but this example seems to show that this assumption does not always hold, and can be compiler-dependent. Is there anything in the standard that specifies what should happen here, or is it better to avoid having functions return non-trivial objects ? Apologies if this question is a duplicate.
In both cases, the compiler generates a copy constructor for you, that has no output so you won't know if it is called: See this question.
In the first case the compiler generated copy constructor is used, which matches the second destructor call. The line return my; calls the copy constructor, giving it the variable my to be used to construct the return value. This doesn't generate any output.
my is then destroyed. Once the function call has completed, the return value is destroyed at the end of the line { function();.
In the second case, the copy for the return is elided completely (the compiler is allowed to do this as an optimisation). You only ever have one My instance. (Yes, it is allowed to do this even though it changes the observable behaviour of your program!)
These are both ok. Although as a general rule, if you define your own constructor and destructor, you should also define your own copy constructor (and assignment operator, and possibly move constructor and move assignment if you have c++11).
Try adding your own copy constructor and see what you get. Something like
My (const My& otherMy) { cout << "My copy constructor\n"; }
The problem is that your class My violates the Rule of Three; if you write a custom destructor then you should also write a custom copy constructor (and copy assignment operator, but that's not relevant here).
With:
struct My
{ My() { cout << "My constructor " << endl; }
My(const My &) { cout << "My copy constructor " << endl; }
~My() { cout << "My destructor " << endl; }
};
the output for MSVC is:
My constructor
My function
My copy constructor
My destructor
My destructor
As you can see, (copy) constructors match with destructors correctly.
The output under gcc is unchanged, because gcc is performing copy elision as allowed (but not required) by the standard.
You are missing two things here: the copy constructor and NRVO.
The behavior seen with MSVC++ is the "normal" behavior; my is created and the rest of the function is run; then, when returning, a copy of your object is created. The local my object is destroyed, and the copy is returned to the caller, which just discards it, resulting in its destruction.
Why does it seem that you are missing a constructor call? Because the compiler automatically generated a copy constructor, which is called but doesn't print anything. If you added your own copy constructor:
My(const My& Right) { cout << "My copy constructor " << endl; }
you'd see
My constructor <----+
My function | this is the local "my" object
My copy constructor <--|--+
My destructor <----+ | this is the return value
My destructor <-----+
So the point is: it's not that there are more calls to destructors than constructors, it's just that you are not seeing the call to the copy constructor.
In the gcc output, you are also seeing NRVO applied.
NRVO (Named Return Value Optimization) is one of the few cases where the compiler is allowed to perform an optimization that alters the visible behavior of your program. In fact, the compiler is allowed to elide the copy to the temporary return value, and construct the returned object directly, thus eliding temporary copies.
So, no copy is created, and my is actually the same object that is returned.
My constructor <-- called at the beginning of f
My function
My destructor <-- called after f is terminated, since
the caller discarded the return value of f
To date, I have been designing my classes on the assumption that for each constructor call there will be at most one destructor call [...]
You can still "assume" that since it is true. Each constructor call will go in hand with exactly one destructor call. (Remember that if you handle stuff on the free/heap memory on your own.)
[..] and can be compiler-dependent [...]
In this case it can't. It is optimization depedant. Both, MSVC and GCC behave identically if optimization is applied.
Why don't you see identical behaviour?
1. You don't track everything that happens with your object. Compiler-generated functions bypass your output.
If you want to "follow-up" on the things your compiler does with your objects, you should define all of the special members so you can really track everything and do not get bypassed by any implicit function.
struct My
{
My() { cout << "My constructor " << endl; }
My(My const&) { cout << "My copy-constructor " << endl; }
My(My &&) { cout << "My move-constructor " << endl; }
My& operator=(My const&) { cout << "My copy-assignment " << endl; }
My& operator=(My &&) { cout << "My move-assignment " << endl; }
~My() { cout << "My destructor " << endl; }
};
[Note: The move-constructor and move-assignment will not be implicitly present if you have the copy ones but it's still nice to see when the compiler use which of them.]
2. You don't compile with optimization on both MSVC and GCC.
If compiled with MSVC++11 /O2 option the output is:
My constructor
My function
My destructor
If compiled in debug mode / without optimization:
My constructor
My function
My move-constructor
My destructor
My destructor
I can't do a test on gcc to verify if there's an option that enforces all of these steps but -O0 should do the trick I guess.
What's the difference between optimized and non-optimized compilation here?
The case without any copy omittance:
The completely "non-optimized" behaviour in this line My my_in_main = function();
(changed the name to make things clear) would be:
Call function()
In function construct My My my;
Output stuff.
Copy-construct my into the return value instance.
return and destroy my instance.
Copy(or move in my example)-construct the return value instance into my_in_main.
Destroy the return value instance.
As you can see: we have at most two copies (or one copy and one move) here but the compilers may possibly omit them.
To my understanding, the first copy is omited even without optimization turned on (in this case), leaving the process as follows:
Call function()
In function construct My My my; First constructor output!
Output stuff. Function output!
Copy(or move in my example)-construct the return value instance into my_in_main. Move output!
Destroy the return value instance. Destroy output!
The my_in_main is destroy at the end of main giving the last Destroy output!. So we know what happens in the non-optimized case now.
Copy elision
The copy (or move if the class has a move constructor as in my example) can be elided.
§ 12.8 [class.copy] / 31
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects.
So now the question is when does this happen in this example? The reason for the elison of the first copy is given in the very same paragraph:
[...] in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value.
Return type matches type in the return statement: function will construct My my; directly into the return value.
The reason for the elison of the second copy/move:
[...] when a temporary class object that has not been bound to a reference (12.2) 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.
Target type matches the type returned by the function: The return value of the function will be constructed into my_in_main.
So you have a cascade here:
My my; in your function is directly constructed into the return value which is directly constructed into my_in_main So you have in fact only one object here and function() would (whatever it does) in fact operate on the object my_in_main.
Call function()
In function construct My instance into my_in_main. Constructor output!
Output stuff. Function output!
my_in_main is still destroyed at the end of main giving a Destructor output!.
That makes three outputs in total: Those you observe if optimization is turned on.
An example where elision is not possible.
In the following example both copies mentioned above cannot be omitted because the class types do not match:
The return statement does not match the return type
The target type does not match the return type of the function
I just created two additional types:
#include <iostream>
using namespace std;
struct A
{
A(void) { cout << "A constructor " << endl; }
~A(void) { cout << "A destructor " << endl; }
};
struct B
{
B(A const&) { cout << "B copy from A" << endl; }
~B(void) { cout << "B destructor " << endl; }
};
struct C
{
C(B const &) { cout << "C copy from B" << endl; }
~C(void) { cout << "C destructor " << endl; }
};
B function() { A my; cout << "function" << endl; return my; }
int main()
{
C my_in_main(function());
return 0;
}
Here we have the "completely non-optimized behaviour" I mentioned above. I'll refer to the points I've drawn there.
A constructor (see 2.)
function (see 3.)
B copy from A (see 4.)
A destructor (see 5.)
C copy from B (see 6.)
B destructor (see 7.)
C destructor (instance in main, destroy at end of main)