Prashant Bhanawat
Prashant Bhanawat

Reputation: 101

Why copy constructor is not called when returned value from overloaded operator is passed

Consider the following scenario

class Integer
{
   long long n;
   public:
   Integer(long long i):n(i){}
   Integer(){cout<<"constructor";}
   void print()
   {
      cout<<n<<endl;
   }
   Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;}
   Integer operator+(Integer b);
};

Integer Integer:: operator+(Integer b)
{
   this->n = this->n + b.n;
   return *this;
}
int main() 
{
   // your code goes here
   Integer a(5);
   Integer b(6);
   Integer c(a+b);//line 1
   return 0;
 }

If a+b was temporary then i understand that copy constructor would not be called. But a+b does not return a temporary. The output i get is

 copy constructor 6  //this is because Integer object is passed as value to operator+
 copy constructor -5232903157125162015 //this is when object is returned by value from operator+

I think there should be one more call when a+b is used to initialize c. Most of the related questions have to do with return value optimization but i can't relate RVO to this.

Upvotes: 2

Views: 95

Answers (4)

NonCreature0714
NonCreature0714

Reputation: 6014

To answer your question, the copy constructor never copies anything and the + operator isn't overloaded correctly. Additionally, the default constructor leaves the Integer class with an uninitialized member, which may cause undefined behaviors (not 100% on that one) which may be causing some issues.

So what happens is, because the copy constructors never actually copy a value to the n member of *this, the default constructor is called, and std::cout displays whatever is at the location of n of 'a.n'; which, if uninitialized, could be anything.

This website from Caltech is good, if imperfect, reference for overloading operators.

Try this.

#include <iostream>

using namespace std;


class Integer{
    long long n;
public:
    Integer(long long i):n(i){}
    //Integer(){cout<<"cosntructor";} //This creates an unitialized  instance of integer.

    Integer(){// Do this instead.
        n = 0;
        cout << "Constructor" << endl;
    }

    void print(){
        cout<<n<<endl;
    }
    //Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;} // This isn't a copy contructor.

    Integer(const Integer &a){ //This is a copy contructor.
        this->n = a.n;
        cout << "Copy constructor" << " " << a.n << endl;
    }

    Integer& operator+=(const Integer &);
    Integer operator+(const Integer &);
};

Integer& Integer::operator+=(const Integer &b){ //Always overload incrementors/decrementors first, makes life easier.
    this->n = this->n + b.n;
    return *this;
}


Integer Integer:: operator+(const Integer &b){
     return Integer(*this)+=b.n; //Notice the use of overloaded incrementor inside the '+' operator.
}

int main(){
    // your code goes here
    Integer a(5);
    Integer b(6);
    Integer c(a+b);//line 1
    return 0;
}

Upvotes: 1

Mohit Jain
Mohit Jain

Reputation: 30489

You are witnessing the copy ellission of the constructor.

When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".

To view the expected output (not eliding of constructor) use -fno-elide-constructors option in gcc.

EDIT

As mentioned in my comment on your question Integer:: operator+ is not quite right. Moreover as mentioned in other answers, your copy constructor doesn't do the initialization of member and thus causes undefined behaviuor.

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148880

You say a+b does not return a temporary. Wrong. a+b (even if it changes a which will certainly mislead an future reader...) returns a temporary copy of a because it is declared to return an Integer and not a reference (Integer&).

So here is what happens:

Integer a(5); // creates an integer from int 5
Integer b(6); // creates an integer from int 6

Integer c(a+b); /* first creates a temp using copy ctor from b
                   updates a
                   creates a temp copy of the result
                   should create an integer by copy of the result (elided)
                   destroys the temporary created from b
*/

return 0; // destroys c, b, a

BTW: you had forgotten to initialize n in your copy ctor - it should be:

Integer(const Integer &a):n(a.n) {cout<<"copy constructor"<<" "<<a.n<<endl;}

Upvotes: 2

ixSci
ixSci

Reputation: 13698

It has a correct number of copy ctor calls:

1st is for b because operator+(Integer b) accepts Integer by value.

2nd is for c created by operator+ result.

By the standard there could be a 3rd call: return *this might have created a temporary which would later be copied to c. But in reality it is always elided. You can turn off this elision in gcc&clang and you can't turn it off in MSVC.

Upvotes: 0

Related Questions