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
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?
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?
In Java, a constructor is the same as a method but the only difference is that the constructor has the same name as the class name. It is used to create an instance of the class. It is called automatically when we create an object of the class. It has no return type.
A function can also return objects either by value or by reference. When an object is returned by value from a function, a temporary object is created within the function, which holds the return value. This value is further assigned to another object in the calling function.
commercial-grade C++ compilers won't do that: the return statement will directly construct x itself. Not a copy of x, not a pointer to x, not a reference to x, but x itself.
In C++, a Copy Constructor may be called for the following cases: 1) When an object of the class is returned by value. 2) When an object of the class is passed (to a function) by value as an argument. 3) When an object is constructed based on another object of the same class.
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);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With