Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Copy Constructor Oddity

I cannot figure out why in the next piece of code, the defined copy constuctor doesn't print...

#include <iostream>
using namespace std;
class B {
   static int count;
   int data; 
   int id;

   void print(const char* p)
   {
       cout <<p <<", "<<id <<", " << data << endl;
   }

   public:

   B(int d=0)
   {
      data=d; id=++count; print("B(int)");
   }
   B( const B& a)
   {
      data=a.data; id=++count; print("B(cost B&)");
   }
  ~B(){print("~B()");}

   operator bool(){ return (bool)data;}
   B operator+(int i){print("operator+"); return B(data+i);}
};


int B::count=0;

void main(){
   B b(42);
   B x=b+2;
   bool z=b+1;

   getchar();
}

I expected to get a copy constructor print with B x=b+2 but it doesn't show. Any ideas? Thanks,


The output:

B(int), 1, 42
operator+, 1, 42
B(int), 2, 44
operator+, 1, 42
B(int), 3, 43
~B(), 3, 43

So it's return value optimization?

like image 310
Alonoaky Avatar asked Nov 13 '22 02:11

Alonoaky


1 Answers

I popped this into GCC really quick and noticed the same behavior. Obviously the compiler is changing the behavior of the code in a way it sees fit.

For one: assignment is not equal to copy constructor. I figured since it was using '=' it was looking for an operator= overload and not finding it and then choosing to create a new B object using its regular constructor and then copying that into x using default assignment. This works since it is a simple class.

I changed

B x=b+2;

to

B x(b+2);

to try to force it to use the copy constructor. However I had the same behavior. I then looked at the return type of the operator+ and noticed it returns a B. (Not a reference but it can be evaluated as a reference as long as it obeys the pre-C++11 rules of references). So again the compiler is generating the code the same way as it did above thus we are not making use of the copy constructor.

If you change

B x(b+2);

to

B x(b);

The compiler figures out that the copy constructor can be used(aka it is able to generate a reference to b).

My guess is that the compiler is generating code because of the return type of the operator+. It probably has something to do with how non-const references are handled as lvalues only in pre C++11. So since the operator+ is being called, it cannot create a const reference from it to pass into the copy constructor(because the result of operator+ is an lvalue). So it uses the regular constructor along with an auto generated assignment operator to do the work you have requested.

Hope this helps.

like image 58
Dean Knight Avatar answered Nov 15 '22 06:11

Dean Knight