I had to polish up my understanding of the circumstances under which constructors are called. During this I stumbled over this example from Microsoft:
//RVO class is defined above in figure 4
#include <stdio.h>
RVO MyMethod (int i)
{
RVO rvo;
rvo.mem_var = i;
throw "I am throwing an exception!";
return (rvo);
}
int main()
{
RVO rvo;
try
{
rvo=MyMethod(5);
}
catch (char* str)
{
printf ("I caught the exception\n");
}
}
The RVO class simply has constructor, copyconsdtuctor and destructor print when they are called. Microsoft states that with thorw commented out and no NRVO the output will be:
I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor
However I can't quite follow. I think that this is what happens:
In main constructor is called for RVO rvo;
In MyMethod constructor is called for RVO rvo;
For return (rvo); the copyconstructor is called
In MyMethod destructor is called for the local RVO
In Main destructor is called for the local rvo
This leaves me with one less destructor call than microsoft proclaims. What am i missing?
For completness the RVO class:
class RVO
{
public:
RVO(){printf("I am in constructor\n");}
RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
~RVO(){printf ("I am in destructor\n");}
int mem_var;
};
if you look carefully at the statement rvo=MyMethod(5);
rvo is assigned by the return object of MyMethod, the return object should be constructed in the scope of the main function. This object is unnamed and is a temporary object. the constructor of such an object is shown in the output that is not obvious on first look.
Don't be confused. Without return-value optimization, three) objects are created (including the temporary used for copying via copy constructor), and therefore three objects are being destroyed (including the temporary once again), so it's all good.
With RVO though, the temporary will not be created while copying (the object will be created in the caller's stack frame directly), and you will see only two constructions and two destructions.
rvo is constructed in main()
rvo is constructed in MyMethod
a unnamed RVO-object is copy-constructed from the rvo in MyMethod by the return statement
all three objects are destroyed
"In main constructor is called for RVO rvo; In MyMethod constructor is called for RVO rvo; For return (rvo); the copyconstructor"
So three objects are constructed, in normal circumstances there should be three destroyed. The returned value also needs to be destructed.
Related
I have been looking into this article about NRVO.
class RVO
{
public:
RVO(){
printf("I am in constructor\n"); }
RVO(const RVO& c_RVO) {
printf("I am in copy constructor\n"); }
~RVO(){
printf("I am in destructor\n"); }
int mem_var;
};
RVO MyMethod(int i)
{
RVO rvo;
rvo.mem_var = i;
return (rvo);
}
int main()
{
RVO rvo;
rvo=MyMethod(5);
}
The output is the following on visual studio and this is how i understand it
I am in constructor // main rvo construction
I am in constructor //MyMethod rvo construction
I am in copy constructor //temporary created inside MyMethod
I am in destructor //Destroying rvo in MyMethod
I am in destructor //Destroying temporary in MyMethod
I am in destructor //Destroying rvo of main
If instead i write the main as
int main()
{
RVO rvo = MyMethod(5);
return 0;
}
The output is the following and how understand it
I am in constructor //MyMethod rvo construction
I am in copy constructor //temporary created inside MyMethod
I am in destructor //Destroying rvo in MyMethod
I am in destructor //Destroying rvo of main
Why is temporary not destroyed in Mymethod in the second version?
Why is copy constructor not called in RVO rvo = MyMethod(5);.I think copy constructor should be called twice in second version, one for the temporary created inside Mymethod and the other for RVO rvo = MyMethod(5);
I know some call may be getting elided.Can someone please help in explaining these calls.
EDIT:
Using return rvo instead of return (rvo) changes the output as
First case
I am in constructor
I am in constructor
I am in destructor
I am in destructor
second case
I am in constructor
I am in destructor
I guess when i removed the parenthesis, then NRVO kicks in.But i am more interested in the first output when there is no optimization
UPDATE: addressing the output of the updated program, using return rvo rather than return (rvo);
I am in constructor
I am in constructor
I am in destructor
I am in destructor
The reason you see this is that both objects (MyMethod::rvo and main::rvo) undergo default construction, then the latter is assigned to as a separate action but you're not logging that.
You can get a much better sense of what is going on by outputting the addresses of the objects, and the this pointer values as functions are called:
#include <cstdio>
#include <iostream>
class RVO
{
public:
RVO(){
printf("%p constructor\n", this); }
RVO(const RVO& c_RVO) {
printf("%p copy constructor, rhs %p\n", this, &c_RVO); }
~RVO(){
printf("%p destructor\n", this); }
int mem_var;
};
RVO MyMethod(int i)
{
RVO rvo;
std::cout << "MyMethod::rvo # " << &rvo << '\n';
rvo.mem_var = i;
return (rvo);
}
int main()
{
RVO rvo=MyMethod(5);
std::cout << "main::rvo # " << &rvo << '\n';
}
The output will also depend on whether you compile with optimisations; you link to Microsoft documentation, so perhaps you're using the Microsoft compiler - try cl /O2.
Why is temporary not destroyed in Mymethod in the second version?
There was no temporary there - the object in main was directly copy-constructed. Stepping you through it:
002AFA4C constructor
MyMethod::rvo # 002AFA4C // MyMethod::rvo's constructed
002AFA70 copy constructor, rhs 002AFA4C // above is copied to 2AFA70
002AFA4C destructor // MyMethod::rvo's destructed
main::rvo # 002AFA70 // turns out the copy above was directly to main::rvo
002AFA70 destructor // main::rvo's destruction
[Alf's comment below] "directly copy-constructed" is not entirely meaningful to me. I think the OP means the rvo local variable
Consider the enhanced output from the first version of the program (without optimisation):
002FF890 constructor // we find out this is main::rvo below
002FF864 constructor // this one's MyMethod::rvo
MyMethod::rvo # 002FF864
002FF888 copy constructor, rhs 002FF864 // 2FF888 is some temporary
002FF864 destructor // there goes MyMethod::rvo
002FF888 destructor // there goes the temporary
main::rvo # 002FF890
002FF890 destructor // and finally main::rvo
If we tie that back in to the OP's output and annotations...
I am in constructor // main rvo construction
I am in constructor //MyMethod rvo construction
I am in copy constructor //temporary created inside MyMethod
I am in destructor //Destroying rvo in MyMethod
I am in destructor //Destroying temporary in MyMethod
I am in destructor //Destroying rvo of main
The OP (correctly) refers to the copy-constructed object as a temporary. When I say of the second version of the program "There was no temporary there - the object in main was directly copy-constructed." - I mean that there's no temporary equivalent to that in the first program we analysed directly above, and instead it's main::rvo that's copy-constructed from MyMethod::rvo.
The first descructor call is from the desctruction of the rvo in main. The object first created has to be deleted. It is not copy assignment which is done, it is copy construction.
I simply created a class like this:
class GreatClass
{
public:
GreatClass(){cout<<"Default Constructor Called!\n";}
GreatClass(GreatClass &gc){cout<<"Copy Constructor Called!\n";}
GreatClass(const GreatClass &gc){cout<<"Copy Constructor (CONST) Called!\n";}
~GreatClass(){cout<<"Destructor Called.\n";}
GreatClass& operator=(GreatClass& gc){cout<<"Assign Operator Called!";return gc;}
const GreatClass& operator=(const GreatClass& gc){cout<<"Assign Operator (CONST) Called!";return gc;}
};
GreatClass f(GreatClass gc)
{
return gc;
}
and in main() function, there are two versions:
version #1:
int main()
{
GreatClass g1;
GreatClass G = f(g1);
}
version #2:
int main()
{
GreatClass g1;
f(g1);
}
They all generates the SAME output:
Default Constructor Called!
Copy Constructor Called!
Copy Constructor Called!
Destructor Called.
Destructor Called.
Destructor Called.
I do not understand why there is nothing happening when I'm assigning f(g1) to G. What constructor or operator is called at this point?
Thanks.
Compiler implementations are allowed to elide/remove copy constructor calls in certain cases, the example you specify is a good example use case of such a scenario. Instead of creating a temporary object and then copying it to destination object the object is created directly in the destination object and the copy constructor call is removed out.
This optimization is known as Copy elision through Return value optimization.
Also, with C++11 move semantics through rvalue references might kick in instead of the Copy semantics. Even with move semantics the compilers are still free to apply RVO.
If I have a function like the following:
stack fillStack(){
stack a;
return a;
}
void main(){
stack s=fillStack();
}
Consider we have a class called stack.
How many constructor and destructor will be called?
Here is what should be happening:
stack fillStack() {
stack a; // constructor
return a; // copy constructor and destructor a
}
int main(){
stack s=fillStack(); // copy constructor and destructor of the temporary
} // destructor s
In practice the standard explicitly allows the copy-constructors to be
optimized away (this is called copy elision) and the value to be
constructed in place. That could end up looking something like this:
void fillStack(stack&);
int main() {
stack s;
fillStack(s); // with reference
}
Although, copy-construction must still be well-formed even if the
compiler applies this transformation. If copy-construction can have
side-effects this optimization can lead to somewhat odd behavior (try
printing something from the copy-constructor and observe the behavior
on different optimization levels).
This optimization becomes largely unnecessary with C++11 due to
move-semantics.
Assuming NO compiler optimization, it should be 2 Copy constructor calls - One from function local to return value temporary, one from return value temporary to s
The following code was compiled and run in Visual Studio 2012 Express for Windows Desktop, as a learning exercise.
#include <cstdio>
class X
{
public:
X() { printf("default constructed\n"); }
~X() { printf("destructed\n");}
X(const X&) { printf("copy constructed\n"); }
X(X&&) { printf("move constructed\n"); }
X & operator= (const X &) { printf("copy assignment operator\n"); }
};
X A() {
X x;
return x;
}
int main() {
{
A();
}
std::getchar();
}
When compiled with compiler optimizations disabled (/Od), the resulting output indicates that the destructor is called twice. This is a problem given that only one object is constructed. Why is the destructor being called twice? Wouldn't this be a problem if the class was managing it own resources?
default constructed
move constructed
destructed
destructed <<< Unexpected call
I tried a couple of experiments to try and explain the output, but ultimately these didn't lead to any useful explanations.
Experiment 1: When the same code is compiled with optimizations enabled (/O1 or /O2), the resulting output is:
default constructed
destructed
which indicates that the Named Return Value Optimization has elided the call to the move constructor, and masked the underlying problem.
Experiment 2: Disabled the optimization and commented out the move constructor. The output generated was what I expected.
default constructed
copy constructed
destructed
destructed
Keep in mind that when an object is the source of a move operation it will still be destroyed. So the source of the move needs to put itself in a state such that being destructed will not release resources that it no longer owns (since they were moved to another object). For example, any raw pointers (that will now be owned by the move constructed object) in the source object should be set to NULL.
The X in A is destroyed when it goes out of scope.
A returns a temporary object (constructed from X by the move constructor) which is a separate instance. This is destroyed in the caller's scope. That will cause the destructor to be called again (on the temporary).
The move constructor was picked because the compiler detected that X was going to be destroyed immediately afterward. To use this approach, the move constructor should nullify or reset any data in the original object so that the destructor will not invalidate any data that has been taken over by the destination of the move.
When you pass an rvalue by value, or return anything by value from a function, the compiler first gets the option to elide the copy. If the copy isn’t elided, but the type in question has a move constructor, the compiler is required to use the move constructor.
http://cpp-next.com/archive/2009/09/move-it-with-rvalue-references/
When you exit from the scope in which the temporary object was created, it is destroyed. If a reference is bound to a temporary object, the temporary object is destroyed when the reference passes out of scope unless it is destroyed earlier by a break in the flow of control.
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr382.htm
The RVO can produce different behavior from the non-optimized version:
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]
http://en.wikipedia.org/wiki/Return_value_optimization
Although Michael's and jspcal answers are accurate, they didn't answer the heart of my question, which was why were there two destructor calls made. I was expecting just one.
The answer is that function A() returns a temporary object. Always. This is how function return values work, and move-semantics has no bearing on this fact. I guess Michael and jspcal assumed that I had not missed such a basic fact. I equated the term "moved" with the concept of "swap". When swapped, objects are not constructed and destructed. Hence I was expecting just one destructor call.
Since the returned object must be constructed and destructed, the second destructor call was made (and a second constructor call).
Now, the actual constructor selected to be executed depends on what is provided in the class definition. If a move-constructor is available, that will be called. Otherwise the copy constructor will be called.
As far as i know, a copy constructor is invoked in the following scenarios :
1) Pass by value
2) Return by value
3) When you create and initialize a new object with an existing object
Here's the program :
#include <iostream>
using namespace std;
class Example
{
public:
Example()
{
cout << "Default constructor called.\n";
}
Example(const Example &ob1)
{
cout << "Copy constructor called.\n";
}
Example& operator=(const Example &ob1)
{
cout << "Assignment operator called.\n";
return *this;
}
~Example()
{
cout<<"\nDtor invoked"<<endl;
}
int aa;
};
Example funct()
{
Example ob2;
ob2.aa=100;
return ob2;
}
int main()
{
Example x;
cout << "Calling funct..\n";
x = funct();
return 0;
}
The output is:
Default constructor called.
Calling funct..
Default constructor called.
Assignment operator called.
Dtor invoked
Dtor invoked
Please correct me, IIRC the following sequence of calls should occur :
1) Constructor of x is called
2) Constructor of ob2 is called
3) The function returns and so copy constructor is invoked (to copy ob2 to unnamed temporary variable i.e funct() )
4) Destructor of ob2 called
5) Assign the unnamed temporary variable to x
6) Destroy temporary variable i.e invoke its destructor
7) Destroy x i.e invoke x's destructor
But then why copy constructor is not invoked and also only 2 calls to dtors are there whereas i expect 3.
I know compiler can do optimizations, however, is my understanding correct ?
Thanks a lot :)
Regards
lali
A copy constructor might not be invoked when you return by value. Some compilers use return value optimization feature.
Read about "Return Value Optimization"
The part of the standard which tells you when compilers may elide copies is 12.8/15. It's always up to the compiler whether to do actually perform the elision. There are two legal situations, plus any combination of them:
"in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type"
"when a temporary class object that has not been bound to a reference would be copied to a class object with the same cv-unqualified type".
The former is usually referred to as the "named return value optimization", and it's what permits the output you're seeing in your example. The latter in effect turns copy-initialization into direct initialization, and could occur for instance if your code did Example x = Example();.
Other copy elisions are not permitted, except of course that the usual "as-if" rules apply. So if the copy constructor has tracing in, then the following code must call it:
Example x;
Example y = x;
But if x were otherwise unused, and the cctor had no side-effects, then I think it could be optimized away, just like any other code that does nothing.
When doing x = funct(); the compiler notices that it will be directly returned and thus avoids a useless construction. That's also why you will only get two destructor calls.
This is a example why sometimes working with "copy" isn't necessarily a lost of performances.
In your exampe the structure is small enough therefore it is passed through a register. The generated code is similar to Return value optimization. Construct a more complicated example, and you'll see the behavior expected.
g++ v4.4.1 has an option to suppress "elide" optimizations:
tst#u32-karmic$ g++ -fno-elide-constructors Example.cpp -o Example
tst#u32-karmic$ ./Example
Default constructor called.
Calling funct..
Default constructor called.
Copy constructor called.
Dtor invoked
Assignment operator called.
Dtor invoked
Dtor invoked
As you can see the copy constructor is now called!