Why is copy constructor also not invoked in the last line? - c++

here is a class definition with 3 different constructors, one default, one integer parametrized and a copy constructor. R-value objects are created and operated on just after creation. The first two cases work as expected but in the final case two constructor invocation is expected but only the first one takes place. Why so? Following is the code.
#include <iostream>
using namespace std;
class A
{
int i;
public:
A() {cout << "In default ctor\n";};
A(int v) {cout << "Setting i in par-ctor to " << v << "\n"; i=v;}
A(const A& o) {cout << "In copy ctor, changing i to " << o.i << "\n"; i=o.i;}
void print() {cout << "i = " << i << endl;}
};
int main()
{
A o(10);
A(o).print(); // invokes copy constructor A(const A&) as expected
A(20).print(); // invokes int parametrized constructor A(int)
A(A(30)).print(); // invokes only int constructor A(int) and not copy constructor, why so?
return(0);
}
The output is:
Setting i in par-ctor to 10
In copy ctor, changing i to 10
i = 10
Setting i in par-ctor to 20
i = 20
Setting i in par-ctor to 30
i = 30

The C++ standard explicitly allows compilers to omit copies in certain situations. This is known as copy elision. One such situation is as you've shown:
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
Note that copy elision is optional. Your compiler happens to be treating that line like A(30).print();, but it doesn't have to.

Related

Shouldn't there be a copy ctor invocation here? Elision disabled (no named return value optimization)

struct Test {
int field = 30;
Test() { cout << "In ctor" << endl; }
Test(const Test &other) { field = other.field; cout << "In copy ctor" << endl; }
Test(Test &&other) { field = other.field; cout << "In move ctor" << endl; }
Test &operator=(const Test &other) { field = other.field; cout << "In copy assignment" << endl; return *this; }
Test &operator=(Test &&other) { field = other.field; cout << "In move assignment" << endl; return *this; }
~Test() { cout << "In dtor" << endl; }
};
Test get_test() {
Test t;
return t;
}
int main() {
Test t2 = get_test();
}
I think this is the canonical NRVO example. I'm compiling with -fno-elide-constructors and I see that the following are called: ctor, move ctor, dtor, dtor.
So the first ctor call corresponds to the line Test t;, the move ctor is constructing the t2 in main, then the temporary that is returned from get_test is destroyed, then the t2 in main is destroyed.
What I don't understand is: shouldn't there be a copy ctor invocation when returning by value? That is, I thought that get_test should be making a copy of t and then this copy is moved into t2. It seems like t is moved into t2 right away.
C++17
Starting from C++17, there is mandatory copy elison which says:
Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:
In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type:
(emphasis mine)
This means that t2 is constructed directly from the prvalue that get_test returns. And since a prvalue is used to construct t2, the move constructor is used. Note that in C++17, the flag -fno-elide-constructors have no effect on return value optimization(RVO) and is different from NRVO.
Pre-C++17
But prior to C++17, there was non-mandatory copy elison and since you've provided the -fno-elide-constructors flag, a temporary prvalue is returned by get_test using the move constructor. So you see the first call to the move ctor. Then, that temporary is used to initialize t2 again using the move constructor and hence we get the second call to the move ctor.

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!

Why copy constructor not called when object passed to a function [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 8 years ago.
I have the following code:
#include <iostream>
using namespace std;
class foo
{
public:
foo(int a, int b) :
a_(a), b_(b)
{ cout << "regular const" << endl; }
foo(const foo& f) :
a_(f.a_), b_(f.b_)
{ cout << "copy const" << endl; }
foo& operator=(const foo& rhs)
{cout << "copy= const" << endl; a_ = rhs.a_; b_ = rhs.b_; return *this; }
int a_, b_;
};
void bar(foo f)
{ }
int main()
{
foo f1(10, 20);
cout << "------" << endl;
bar(f1);
cout << "------" << endl;
bar(foo(11, 22)); // line 29
cout << "------" << endl;
bar({13, 23}); // line 32
cout << "------" << endl;
}
I get the following output:
$ ./a.out
regular const
------
copy const
------
regular const
------
regular const
------
For line 29 and line 32, I was expecting a temp object to be created in main (invoking regular constructor) and then a copy constructor being invoked when passed to bar(). From the output I see the compiler doing some optimization, and guessing maybe just creating the object on the stack when calling bar() and only regular constructor being invoked. Can someone please help me understand what type of optimization is being done or what is happening under the hood.
Is calling bar() using lines 29 and 32 equivalent as far as generated code? I understand line 29 is more readable.
I changed bar() as follows:
void bar(const foo& f)
{ }
I get the same output for lines 29 and 32. In this case where is the object being created?
Thank you,
Ahmed.
In line 32,
bar({13, 23});
the parameter is initialized per copy-list-initialization - no intermediate temporary is created, not even theoretically.
bar(foo(11, 22));
Here, copy elision is involved. The compiler is allowed to elide the temporary, that is, construct the object directly into the parameter:
This elision of copy/move operations, called copy elision, is
permitted in the following circumstances (which may be combined to
eliminate multiple copies): […] — 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
If you want to see the output without any elided copies or moves, use -fno-elide-constructors with GCC. It will produce the output you expect. At least for line 32.
Now to the second version of bar:
void bar(const foo& f)
In line 29, a temporary is created and initialized from the braced-init-list. In line 32, the reference is bound to the temporary argument. No optimizations or elisions are involved.
bar({13, 23}); // A temporary object is created as if by foo{13, 23},
// and the reference is bound to it
bar(foo(11, 22)); // The reference is bound to the temporary.
// The lifetime of the temporary has been extended.

C++ copy constructor behaviour [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 8 years ago.
There is a part of C++ code I don't really understand.
Also I don't know where should I go to search information about it, so I decided to ask a question.
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test();
Test(Test const & src);
Test& operator=(const Test& rhs);
Test test();
int x;
};
Test::Test()
{
cout << "Constructor has been called" << endl;
}
Test::Test(Test const & src)
{
cout << "Copy constructor has been called" << endl;
}
Test& Test::operator=(const Test& rhs)
{
cout << "Assignment operator" << endl;
}
Test Test::test()
{
return Test();
}
int main()
{
Test a;
Test b = a.test();
return 0;
}
Why the input I get is
Constructor has been called
Constructor has been called
?
a.test() creates a new instance by calling "Test()" so that's why the second message is displayed. But why no copy constructor or assignment called?
And if I change "return Test()" to "return *(new Test())" then the copy constructor is called.
So why isn't it called the first time?
Compilers are very smart. Both copies - returning from test and initialising b (not this is not an assignment) - are elided according to the following rule (C++11 §12.8):
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
Compilers are allowed to do this even if it would change the behaviour of your program (like removing your output messages). It's expected that you do not write copy/move constructors and assignment operators that have other side effects.
Note that is only one of four cases in which copy elision can occur (not counting the as-if rule).
The call to a.test() returns by value and this value is then assigned to b "copying" the return value. This invokes the copy constructor.

How to prove that Copy Constructor is mandatory

I Have just created a class with an integer variable and a pointer variable. After creating its object , I passed it to a function. Even after returning the function the program is not throwing the exception
#include"iostream"
using namespace std;
class A
{
public :
int i;
char *c;
void show();
};
void func(A obj);
int main()
{
A a;
a.i = 10;
a.c = "string";
cout << " Before Fun " << endl;
a.show();
cout << " Going To Call func " << endl;
func(a);
cout << " After func " << endl;
a.show();
return 0;
}
void A::show()
{
cout << " The valuses in Object are " << i << '\t' << c << endl;
}
void func(A aa)
{
cout << " The valuses in Object are " << aa.i << '\t' << aa.c << endl;
}
In The Func I am passing the object a (from main) and it would get copied in aa (stack of func). so after returning from the func if i call show ( the pointer c would be null of a), It would give me exception
But it is not happening . please help me to prove the requirement of copy constructor
Hide the copy constructor. That will cause a compilation error everywhere it is called implicitly.
class A
{
public :
int i;
char *c;
private:
A(const A& _other);
};
If no copy constructor is declared for an object, one is implicitly defined. This copy constructor copies each element of the object.
In your example, the call to func(a) will call this copy constructor, and so aa will be a copy of a (aa.i will be 10 and aa.c will point to the first element of "string").
If you are using C++11 you can do the following to remove the copy constructor
class A
{
public :
int i;
char *c;
void operator=(const A& _other) = delete;
A(const A& _other) = delete;
};
or even better:
class A : public NonCopyable // perhaps std:: or if you prefer boost, boost::
{
public :
int i;
char *c;
};
http://en.wikipedia.org/wiki/C++11#Explicitly_defaulted_and_deleted_special_member_functions
When you make a class, a null constructor and copy constructor exist implicitly.
So that is why it does not throw an exception.
However, if you define ANY constructor, you will then need to define others otherwise the rest of the constructors will be overwritten.
For example, you define a null constructor only. Then it will throw an exception. Because the implicitly defined copy constructor will be overridden.
This is one way of proving the need of a copy constructor.
An implicit copy constructor does exist if you do not define one explicitly. It is called in your func() function. A object is copied and assigned a value for its member i.
Result will be: that you do get with show() the value for i that you had before when calling func. This is with no surprise because you are not doing any assignment to members of A inside A.
Implicit copy constructor by the compiler provide member-wise copy. This is what you may need in most cases.
When is default constructor not sufficient ? you have pointer like members, and need a deep-copy of it. You don't want to copy the pointer (what copy constructor would do), but rather to have the object pointed to copied. But why do you need deep copy ? You don't want to copy the pointer (which is owned by any instance of the class), and end up calling twice destructor on the same pointer. From the second call to delete on this pointer, heap corruption ensues. You may also need copy constructor made explicit for shared_pointers.
Note that, for performance reasons, it is best pass object by const reference than by value.
In your case the copy constructor provided by compiler is present.
Modify your code and write a copy constructor as:
//Default and copy constructor
A()
{
i = 0;
c = NULL;
}
A(const A& _other)
{
cout<<"In copy cons"<<endl;
}
When you will modify the code as per above when func gets called then your own copy constructor gets called in this case you will get the junk values(can not predict)
And if you modify your copy constructor code as
A(const A& _other)
{
cout<<"In copy cons"<<endl;
i = _other.i;
c = new char [(strlen(_other.c) +1)];
strcpy(c,_other.c);
}
Now you can see the differnce in both outputs and the use of Copy constructor how and where it's used.