6

I wanted try out what I read about returning by value in C++ (that it's the same as with passing by value in that new object is create) I have code like this:

#include <iostream>

using namespace std;

class Kar{

public:
    int n;
    static int no;

    Kar(){
        n = ++Kar::no;
        cout << "Creating Kar " << n << endl;
    }

    Kar(Kar &k){
        n = ++Kar::no;
        cout << "Copying Kar " <<k.n<< " to new Kar " << n << endl;
    }

    ~Kar(){
        cout << "Destroying Kar "<< n << endl;
    }

    Kar& operator= (const Kar &k);
};


Kar& Kar::operator= (const Kar &k){

    cout << "Assigning Kar "<< k.n <<" to Kar "<< this->n << endl;
    return *this;
}



int Kar::no;

Kar foo(){
    cout << "Starting foo()" << endl;
    Kar k;
    cout << "Finishing foo()" << endl;
    return k;
}


int main(int argc, char **argv) {
    cout << "Starting!" << endl;

    Kar k;
    k=foo();
    //     Kar k2 = foo();

    cout << "Finishing!" << endl;
    return 0;
}

Terminal output is this:

Starting!
Creating Kar 1
Starting foo()
Creating Kar 2
Finishing foo()
Assigning Kar 2 to Kar 1
Destroying Kar 2
Finishing!
Destroying Kar 1
  1. I would expect the behavior in foo()to be: a. create Kar 2 b. copy it to Kar 3 and return it (subsequently assigning Kar 3 to Kar 1). Why not?

  2. If I uncomment Kar k2 = foo(); I get compiler message:

    error: no matching function for call to Kar::Kar(Kar)

End of course I cannot add constructor Kar(Kar k){ } because that's invalid. What does this mean? Why is not constructor Kar(Kar &k) used for this case?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
ps-aux
  • 11,627
  • 25
  • 81
  • 128
  • 2
    i cant say better than [this](http://definedbehavior.blogspot.in/2011/08/value-semantics-copy-elision.html) and [this](http://definedbehavior.blogspot.in/2011/08/value-semantics-nrvo.html). excellent read – Koushik Shetty Jan 26 '14 at 11:45

2 Answers2

5

The behavior you are seeing is called the return value optimization. So instead of the compiler creating a temp object for the return value, it will eliminate that, and use the object already created after the return statement.

Regarding your second question, you are getting a compiler error because you can't bind non const temporaries (r-values) to l-value references (BTW MSVC accepts this but it's a non standard behavior). Try changing your copy constructor to this:

Kar(const Kar& kar);

concept3d
  • 2,248
  • 12
  • 21
3

The compiler is most likely using an optimisation called copy elision, which is used to avoid unnecessary object copying where possible. This is quite common when returning objects by value. In your case, foo() doesn't need to retain its instance of Kar, because it's destroyed immediately after the return statement; main() doesn't operate on its instance of Kar() until after foo() has returned. It's therefore safe to use one instance for both.

You can read more information about it on this question: What are copy elision and return value optimization?

EDIT: Didn't spot your second question at first. Copy constructors should always take a const reference parameter, i.e. const Kar & in your case, otherwise it ends up trying to take a non-const reference to an r-value, which isn't allowed.

Community
  • 1
  • 1
Peter Bloomfield
  • 5,578
  • 26
  • 37