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!
Related
I'm observing a strange behavior I don't quite understand with respect to destructors and (default) copy and move assignments.
Let's say I have a class B that has default everything and a class Test that has custom destructor, default copy assignment, and (potentially) default move assignment.
Then we create an instance of B, assign it to a variable, and replace with a new instance using assignment (where the right side is rvalue).
Two things seems weird to me and I can't see the reason for them in documentation.
When Test doesn't have move assignment (thus its copy assignment is called) the destructor of T1 object isn't explicitely called. I assume that in this case the idiomatic thing to do is to clean the resources as part of the copy assignment. Why is it different when move assignment is there (and called), however? If its there the Test's destructor is called explicitely (?by the operator).
The documentation specifies that the other after move assignment can be left in whatever state. How come the destructor for T2's temporal rvalue (i.e. the right side of =B("T2")) isn't called in case B's member doesn't have move assignment?
Playground code: https://onlinegdb.com/S1lCYmkKOV
#include <iostream>
#include <string>
class Test
{
public:
std::string _name;
Test(std::string name) : _name(name) { }
~Test()
{
std::cout << "Destructor " << _name << std::endl;
}
Test& operator=(const Test& fellow) = default;
//Test & operator= ( Test && ) = default;
};
class B {
public:
Test t;
B() : t("T0") {}
B(std::string n) : t(n) {}
};
int fce(B& b)
{
std::cout << "b = B(T2)\n";
b = B("T2");
std::cout << "return 0\n";
return 0;
}
int main() {
B b("T1");
std::cout << "fce call\n";
fce(b);
std::cout << "fce end " << b.t._name << std::endl;
}
Output with move:
fce call
b = B(T2)
Destructor T1
return 0
fce end T2
Destructor T2
Output without move:
fce call
b = B(T2)
Destructor T2
return 0
fce end T2
Destructor T2
Default move assignment calls destructor, copy assignment doesn't
Both assignments result in the destruction of a temporary B object, so the destructor is called.
replace with a new instance using assignment
Pedantic note: Assignment doesn't replace instances. The instance remains the same; the value of the instance is modified. This distinction may be subtle, but may also be relevant to your confusion.
When Test doesn't have move assignment (thus its copy assignment is called) the destructor of T1 object isn't explicitely called.
It's somewhat unclear what you mean by "T1 object". The variable b that you initialised with "T1" is destroyed. But when it is destroyed, it's value has previously been assigned to "T2", so that's what the destructor inserts into cout. This happens in both move and copy cases, and this is the second Destructor TX line in the output.
Why is it different when move assignment is there (and called), however?
The difference is when the temporary object from the line b = B("T2") is destroyed. This is the first Destructor TX line in the output.
After copy assignment, this temporary will still hold the "T2" value, so that's what you see in the destructor.
After move assignment, the temporary is no longer guaranteed to contain "T2", but rather it is left in a valid but unspecified state (as described in specification of std::string), so output could be anything. In this case it happened to be "T1". (Based on this result, we might make a guess that the move assignment operator of the string may have been implemented by swapping the internal buffers. This observation is not a guaranteed behaviour).
The documentation specifies that the other after move assignment can be left in whatever state. How come the destructor for T2's temporal rvalue (i.e. the right side of =B("T2")) isn't called in case B's member doesn't have move assignment?
The destructor of the temporary is called. The temporary simply is no longer in the state described by "contains "T2"" after it has been moved from.
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)
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.
(I'm using gcc with -O2.)
This seems like a straightforward opportunity to elide the copy constructor, since there are no side-effects to accessing the value of a field in a bar's copy of a foo; but the copy constructor is called, since I get the output meep meep!.
#include <iostream>
struct foo {
foo(): a(5) { }
foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; }
int a;
};
struct bar {
foo F() const { return f; }
foo f;
};
int main()
{
bar b;
int a = b.F().a;
return 0;
}
It is neither of the two legal cases of copy ctor elision described in 12.8/15:
Return value optimisation (where an automatic variable is returned from a function, and the copying of that automatic to the return value is elided by constructing the automatic directly in the return value) - nope. f is not an automatic variable.
Temporary initializer (where a temporary is copied to an object, and instead of constructing the temporary and copying it, the temporary value is constructed directly into the destination) - nope f is not a temporary either. b.F() is a temporary, but it isn't copied anywhere, it just has a data member accessed, so by the time you get out of F() there's nothing to elide.
Since neither of the legal cases of copy ctor elision apples, and the copying of f to the return value of F() affects the observable behaviour of the program, the standard forbids it to be elided. If you got replaced the printing with some non-observable activity, and examined the assembly, you might see that this copy constructor has been optimised away. But that would be under the "as-if" rule, not under the copy constructor elision rule.
Copy elision happens only when a copy isn't really necessary. In particular, it's when there's one object (call it A) that exists for the duration of the execution of a function, and a second object (call it B) that will be copy constructed from the first object, and immediately after that, A will be destroyed (i.e. upon exit from the function).
In this very specific case, the standard gives permission for the compiler to coalesce A and B into two separate ways of referring to the same object. Instead of requiring that A be created, then B be copy constructed from A, and then A be destroyed, it allows A and B to be considered two ways of referring to the same object, so the (one) object is created as A, and after the function returns starts to be referred to as B, but even if the copy constructor has side effects, the copy that creates B from A can still be skipped over. Also, note that in this case A (as an object separate from B) is never destroyed either -- e.g., if your dtor also had side effects, they could (would) be omitted as well.
Your code doesn't fit that pattern -- the first object does not cease to exist immediately after being used to initialize the second object. After F() returns, there are two instances of the object. That being the case, the [Named] Return Value Optimization (aka. copy elision) simply does not apply.
Demo code when copy elision would apply:
#include <iostream>
struct foo {
foo(): a(5) { }
foo(const foo& f): a(f.a) { std::cout << "meep meep!\n"; }
int a;
};
int F() {
// RVO
std::cout << "F\n";
return foo();
}
int G() {
// NRVO
std::cout << "G\n";
foo x;
return x;
}
int main() {
foo a = F();
foo b = G();
return 0;
}
Both MS VC++ and g++ optimize away both copy ctors from this code with optimization turned on. g++ optimizes both away even if optimization is turned off. With optimization turned off, VC++ optimizes away the anonymous return, but uses the copy ctor for the named return.
The copy constructor is called because a) there is no guarantee you are copying the field value without modification, and b) because your copy constructor has a side effect (prints a message).
A better way to think about copy elision is in terms of the temporary object. That is how the standard describes it. A temporary is allowed to be "folded" into a permanent object if it is copied into the permanent object immediately before its destruction.
Here you construct a temporary object in the function return. It doesn't really participate in anything, so you want it to be skipped. But what if you had done
b.F().a = 5;
if the copy were elided, and you operated on the original object, you would have modified b through a non-reference.
This question already has answers here:
Is there a difference between copy initialization and direct initialization?
(9 answers)
Closed 1 year ago.
Simple question: are the following statements equivalent? or is the second one doing more implicit things behind the scenes (if so, what?)
myClass x(3);
myClass x = myClass(3);
Thanks!
They are not completely identical. The first is called "direct initialization" while the second is called "copy initialization".
Now, the Standard makes up two rules. The first is for direct initialization and for copy initialization where the initializer is of the type of the initialized object. The second rule is for copy initialization in other cases.
So, from that point of view both are termed in one - the first - rule. In the case where you have copy initialization with the same type, the compiler is allowed to elide a copy, so it can construct the temporary you create directly into the initialized object. So you can end up very well with the same code generated. But the copy constructor, even if the copy is elided (optimized out), must still be available. I.e if you have a private copy constructor, that code is invalid if the code in which it appears has no access to it.
The second is called copy-initialization, because if the type of the initializer is of a different type, a temporary object is created in trying to implicitly convert the right side to the left side:
myclass c = 3;
The compiler creates a temporary object of the type of myclass then when there is a constructor that takes an int. Then it initializes the object with that temporary. Also in this case, the temporary created can be created directly in the initialized object. You can follow these steps by printing messages in constructors / destructors of your class and using the option -fno-elide-constructors for GCC. It does not try to elide copies then.
On a side-note, that code above has nothing to do with an assignment operator. In both cases, what happens is an initialization.
The second one may or may not call for an extra myclass object construction if copy elision is not implemented by your compiler. However, most constructors, have copy elision turned on by default even without any optimization switch.
Note initialization while construction never ever calls the assignment operator.
Always, keep in mind:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
In the second one, a temporary object is created first and then is copied into the object x using myClass's copy constructor. Hence both are not the same.
I wrote the following to try and illustrate understand what's going on:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
When I compile and run this code I get the following output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
This would seem to indicate that there is no difference between the two calls made in the main function, but that would be wrong. As litb pointed out, the copy constructor must be available for this code to work, even though it gets elided in this case. To prove that, just move the copy constructor in the code above to the private section of the class definition. You should see the following error:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Also note that the assignment operator is never called.