why compiler is not eliding away copy construction in this case - c++

class test
{
public:
int data;
test(int var = 0):data(var){cout<<"constructor"<<endl;}
~test(){ cout<<"destructor"<<endl; }
test(const test& var)
{
cout<<"copy constructor"<<endl;
this->data = var.data;
}
test& operator=( const test& var)
{
cout<<"assignment op"<<endl;
this->data = var.data;
return *this;
}
};
test passByref_returnByVal(test& obj)
{
return obj;
}
int main()
{
test o1(5);
test o2 = passByref_returnByVal(o1);
cout<<"=========================="<<endl;
test o3;
o3 = passByref_returnByVal(o1);
}
Output:
constructor
copy constructor
constructor
copy constructor
assignment op
destructor
In the given example , object o2 is directly copy constructed without using any temporaries.
But in the second case where i want o3 to be assigned the return value of the function, first a temporary is created using the copy constructor and then assignment operator is called for value assignment.
My question is what was the need for this temporary as my assignment operator takes reference. I found related questions but they didnt answer this.

test o3;
will cause a call to the constructor to create the object. C++ isn't Java where an object type declaration only declares a references but doesn't instantiate an object.
test passByref_returnByVal(test& obj) {....}
causes the copy constructor call because when inside of it you do return obj; the compiler needs to create a temporary object because the return type of that function is test (as opposed to test& or test*).
Finally, because o3 already exists courtesy of the
test o3;
declaration, the assignment operator is called to assign the return value of passByref_returnByVal to the already-existing o3.
So the copy constructor call is happening in passByref_returnByVal, not in your operator=.

Related

why copy constructor called but not move constructor in c++11?

This is the C++ test I do. I thought move constructor would be called, but it's not. neither the two cases will call move constructure.
class MyTest
{
public:
MyTest() {}
~MyTest() {}
MyTest(const MyTest& lv) {} // be called actually
MyTest(MyTest&& rv) {} // not be called actually, but i thought would
void operator=(const MyTest& lv) {
MyTest(std::move(lr)); // i thought the move constructor would be called here
}
};
int main()
{
// case1:
MyTest m1;
MyTest m2 = m1;
// case2:
MyTest m3;
MyTest m4;
m4 = m3; // move constructure will not be called either.
return 0;
}
Your copy assignment operator
void operator=(const MyTest& lv) {
MyTest(std::move(lr));
}
have three problems:
The argument is named lv but you use lr inside the function;
The argument lv is a reference to a constant MyTest object, it can't be "moved" from;
And the statement
MyTest(std::move(lv)); // Corrected the variable name
doesn't make much sense on its own.
It creates a temporary object by rvalue initialization. But since the object isn't used any more the compiler is free to optimize it away making the body of the assignment operator empty.
If you look at the generated code you will most likely see the object creation missing.

Copy constructor is called instead of move constructor - why?

I have this code, taken from here by the way http://www.cplusplus.com/doc/tutorial/classes2/
// move constructor/assignment
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Example6
{
string* ptr;
public:
Example6(const string& str) :
ptr(new string(str))
{
cout << "DONT MOVE " << '\n';
}
~Example6()
{
delete ptr;
}
// move constructor
Example6(Example6&& x) :
ptr(x.ptr)
{
cout << "MOVE " << '\n';
x.ptr = nullptr;
}
// move assignment
Example6& operator=(Example6&& x)
{
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
return *this;
}
// access content:
const string& content() const
{
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs)
{
return Example6(content() + rhs.content());
}
};
int main()
{
Example6 foo("Exam");
Example6 bar = Example6("ple"); // move-construction
foo = foo + bar; // move-assignment
cout << "foo's content: " << foo.content() << '\n';
return 0;
}
I only added output in constructor to see which is being called. To my surprise it is always the first one, copy constructor. Why does it happen? I did some research and found some info about elision. Is it somehow possible to prevent it and always call move constructor?
Also, as a side note, as I said this code is from cplusplus.com. However, I read about move semantics in some other places and I wonder if this move constructor here is done right. Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move, because x is rvalue reference that has a name, so it is really lvalue and we need to use move to cast it to be rvalue. Do i miss something, or is it really tutorial's mistake?
Btw, adding move doesn't solve my first problem.
So anything with a name is an lvalue.
An rvalue reference with a name is an lvalue.
An rvalue reference will bind to rvalues, but it itself is an lvalue.
So x in ptr(x.ptr) is an rvalue reference, but it has a name, so it is an lvalue.
To treat it as an rvalue, you need to do ptr( std::move(x).ptr ).
Of course, this is mostly useless, as moving a ptr does nothing as ptr is a dumb raw pointer.
You should be following the rule of 0 here.
class Example6 {
std::unique_ptr<string> ptr;
public:
Example6 (string str) : ptr(std::make_unique<string>(std::move(str))) {cout << "DONT MOVE " << '\n';}
Example6():Example6("") {}
~Example6 () = default;
// move constructor
Example6 (Example6&& x) = default;
// move assignment
Example6& operator= (Example6&& x) = default;
// access content:
const string& content() const {
if (!ptr) *this=Example6{};
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs) {
return Example6(content()+rhs.content());
}
};
because business logic and lifetime management don't belong intermixed in the same class.
While we are at it:
// addition:
Example6& operator+=(const Example6& rhs) & {
if (!ptr) *this = Example6{};
*ptr += rhs.content();
return *this;
}
// addition:
friend Example6 operator+(Example6 lhs, const Example6& rhs) {
lhs += rhs;
return lhs;
}
Copy constructor is called ... - why?
The premise of your question is faulty: The copy constructor is not called. In fact, the class is not copyable.
The first constructor is a converting constructor from std::string. The converting constructor is called because Example6 objects are initialised with a string argument. Once in each of these expressions:
Example6 foo("Exam")
Example6("ple")
Example6(content() + rhs.content()
... instead of move constructor
There are a few copy-initialisations by move in the program. However, all of them can be elided by the compiler.
Is it somehow possible to prevent it and always call move constructor?
There are a few mistakes that can prevent copy elision. For example, if you wrote the addition operator like this:
return std::move(Example6(content()+rhs.content()));
The compiler would fail to elide the move and probably tell you about it if you're lucky:
warning: moving a temporary object prevents copy elision
Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
There's no need. Moving a pointer is exactly the same as copying a pointer. Same holds for all fundamental types.
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move
ptr is not a string. It is a pointer to a string. Copying a pointer does nothing to the pointed object.
PS. The example program is quite bad quality. There should never be owning bare pointers in C++.
I can say your class does not have a copy constructor.
Because copy ctor parameter have to be const and reference
class Example6{
public:
Example6(const Example6 &r);
};

Confusion about Copy Constructor in C++

Code:
class A
{
public:
A()
{
cout<<"Defualt Constructor"<<endl;
}
A(A &t)
{
cout<<"Copy Constructor"<<endl;
}
};
A func()
{
cout<<"In func"<<endl;
}
int main()
{
A a1;
A a2;
a2 = func();
return 0;
}
The program works fine. Also, If I call function like this:
A a2 = func();
and add const qualifier in copy constructor argument like:
A(const A &t)
{
cout<<"Copy Constructor"<<endl;
}
Also, working fine.
But, If remove const from copy constructor argument like:
A(A &t)
{
cout<<"Copy Constructor"<<endl;
}
and call function func()
A a2 = func();
Compiler giving an error:
error: invalid initialization of non-const reference of type 'A&' from an rvalue of type 'A'
A a2 = func();
^
prog.cpp:13:9: note: initializing argument 1 of 'A::A(A&)'
A(A &t)
^
Why does compiler give an error in last case?
A a2 = func(); is copy initialization, a2 will be initialized from the object returned by func() via copy constructor. func() returns by value, so what it returns is a temporary, which can't be bound to lvalue-reference to non-const (i.e. A &), that's why you get the error.
Temporary could be bound to lvalue-reference to const (or rvalue-reference), so changing the parameter type to const A &t (or adding move constructor) would make it work fine.
BTW: a2 = func(); has nothing to do with copy constructor, but copy assignment operator. You didn't declare it for A, and the implicitly declared copy assignment operator takes const A& as parameter, then it's fine.
BTW2: func() returns nothing. Note that flowing off the end of a non-void function without returning leads to UB.

C++ overriding the assignment operator

To understand constructor and assignment, I wrote a very simply testing code like this:
class A {
public:
A() { std::cout<<"This is default cstr."; }
A(int i) { std::cout<<"This is int cstr. value is "<<i; }
A(const A &a) { std::cout<<"This is copy cstr."; }
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;// this line is tricky }
};
int _tmain(int argc, _TCHAR* argv[]) {
std::cout<<"line 1 "; A a1; std::cout<<std::endl;
std::cout<<"line 2 "; A a2 = A(1); std::cout<<std::endl;
std::cout<<"line 3 "; a1 = a2; std::cout<<std::endl;
return 0;
}
For line 3 I got:
line 3 This is assignment operator.This is copy cstr.
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
Could someone explain what happened inside for me?
Your operator is returning A instead of A&:
A operator=(const A &a)
So when you return NULL, you are calling the implicit constructor A(int) and passing NULL to it.
The problem
line 3 This is assignment operator.This is copy cstr.
Your code's calling:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return *this;
This obviously prints "This is assignment operator.", then return *this; statement sees the return type of A and creates a return value of type A doing the equivalent of A(*this); -> that calls the copy constructor, explaining this part of the output:
line 3 This is assignment operator.This is copy cstr.
^^^^^^^^^^^^^^^^^^
But if I change return *this; to return NULL, I got:
line 3 This is assignment operator.This is int cstr. value is 0
In this case:
A operator=(const A &a) { std::cout<<"This is assignment operator."; return NULL; }
You end up creating the return value of type A as per A(NULL), and as NULL is 0, that matches the A(int) constructor best, which is why you see:
line 3 This is assignment operator.This is int cstr. value is 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The solution
A& operator=(const A &a) { std::cout<<"This is assignment operator."; return *this; }
^
You normally want the assignment operator to return a reference to the *this object. That way, no additional A object is constructed as the assignment operator function returns.
Aside - why return `A&` anyway?
The reason A& is returned and not void, is that it allows further chained use of the object, as in:
a1 = a2 = a3;
Which is evaluated as:
a1.operator=(a2.operator=(a3));
Is a2.operator= returned void then there'd be no usable argument to a1.operator=().
A non-const reference supports usage like:
make_uppercase(my_string = other_string);
In some other languages, that would need to be broken into two statements. Whether you wish it was depends on whether you find it confusing, and how much you value concision.
Your code says
A operator = (const A& a)
You take a reference to an A, you modify yourself, and then you return A(*this) which invokes the copy-constructor to create a new instance and return by value.
What you probably intended was
A& operator = (const A& a)
This will then return a reference to *this rather needing to copy it into a new temporary instance.
Be aware that NULL is a macro alias for '0UL' or '0ULL' which the compiler detects as being a match for A(int). This is one of the reasons that C++11 introduced nullptr as an alternative to NULL.

C++ : Implementing copy constructor and copy assignment operator

After reading about copy constructors and copy assignment operators in C++, I tried to create a simple example. Though the below snippet apparently works, I am not sure whether I am implementing the copy constructor and copy assignment operator the right way. Could you please point out if there are any mistakes/improvements or a better example to understand the relevant concepts.
class Foobase
{
int bInt;
public:
Foobase() {}
Foobase(int b) { bInt = b;}
int GetValue() { return bInt;}
int SetValue(const int& val) { bInt = val; }
};
class Foobar
{
int var;
Foobase *base;
public:
Foobar(){}
Foobar(int v)
{
var = v;
base = new Foobase(v * -1);
}
//Copy constructor
Foobar(const Foobar& foo)
{
var = foo.var;
base = new Foobase(foo.GetBaseValue());
}
//Copy assignemnt operator
Foobar& operator= (const Foobar& other)
{
if (this != &other) // prevent self-assignment
{
var = other.var;
base = new Foobase(other.GetBaseValue());
}
return *this;
}
~Foobar()
{
delete base;
}
void SetValue(int val)
{
var = val;
}
void SetBaseValue(const int& val)
{
base->SetValue(val);
}
int GetBaseValue() const
{
return(base->GetValue());
}
void Print()
{
cout<<"Foobar Value: "<<var<<endl;
cout<<"Foobase Value: "<<base->GetValue()<<endl;
}
};
int main()
{
Foobar f(10);
Foobar g(f); //calls copy constructor
Foobar h = f; //calls copy constructor
Foobar i;
i = f;
f.SetBaseValue(12);
f.SetValue(2);
Foobar j = f = z; //copy constructor for j but assignment operator for f
z.SetBaseValue(777);
z.SetValue(77);
return 1;
}
Your copy assignment operator is implemented incorrectly. The object being assigned to leaks the object its base points to.
Your default constructor is also incorrect: it leaves both base and var uninitialized, so there is no way to know whether either is valid and in the destructor, when you call delete base;, Bad Things Happen.
The easiest way to implement the copy constructor and copy assignment operator and to know that you have done so correctly is to use the Copy-and-Swap idiom.
Only Foobar needs a custom copy constructor, assignment operator and destructor. Foobase doesn't need one because the default behaviour the compiler gives is good enough.
In the case of Foobar you have a leak in the assignment operator. You can easily fix it by freeing the object before allocating it, and that should be good enough. But if you ever add a second pointer member to Foobar you will see that that's when things get complicated. Now, if you have an exception while allocating the second pointer you need to clean up properly the first pointer you allocated, to avoid corruption or leaks. And things get more complicated than that in a polynomial manner as you add more pointer members.
Instead, what you want to do is implement the assignment operator in terms of the copy constructor. Then, you should implement the copy-constructor in terms of a non-throwing swap function. Read about the Copy & Swap idiom for details.
Also, the default constructor of Foobar doesn't default-initialize the members. That's bad, because it's not what the user would expect. The member pointer points at an arbitrary address and the int has an arbitrary value. Now if you use the object the constructor created you are very near Undefined Behaviour Land.
I have a very simple patch for you:
class Foobar
{
int var;
std::unique_ptr<FooBase> base;
...
That should get you started.
The bottom line is:
Don't call delete in your code (Experts see point 2)
Don't call delete in your code (you know better...)