Your suspicions are unfounded.
No copying is required.
Suppose, instead, that you write:
MFVec foo(const MFVec& z1, const MFVec& z2) {
MFVec res = z1;
res += z2
return res;
}
In the typical implementation, the compiler will emit code similar to:
MFVec& foo(MFVec& res, const MFVec& z1, const MFVec& z2) {
res = z1; // using the copy constructor
res += z2;
return res;
}
In other words,
the compiler creates the return value in the calling program
and passes a [hidden] reference to it to function foo.
Function foo does *not* create a local object named res
but recognizes that res is another name for the return value.
This is especially efficient if you use foo to initialize an object
in the calling program:
MFVec z3 = foo(z1, z2);
The compiler emits code to allocate storage for z3
that passes a reference to z3 as a hidden argument to foo
which foo interprets as a reference to the return value --
z3 is initialized by foo directly and no temporary is required.
All that's happening here is assignment.)
Actually the copy constructor is used here, "newly created
variables" includes temporary objects. When the function
returns, a temporary object is created (in the scope of the
calling function) using the copy-constructor, with the returned
value as parameter.
The calling function usually does something with this object,
eg. if it initializes another object then the copy-constructor
will be called again, with the temporary return-value object as
a parameter.
Being a temporary object, it will be destroyed at the end of the
full-expression it was created in, eg if we have at local scope:
Foo func() { Foo g; return g; }
Foo f( func() );
then we have:
- g is default-constructed
- temporary return value is copy-constructed from g
- g is destroyed
- f is copy-constructed from temporary return-value
- temporary return-value is destroyed
The compiler is allowed to optimise all this, so you can't test
it with a compiler, but if you make Foo with a private copy
constructor then it should fail to compile.
Let's try that:
#include <iostream>
class Foo {
private:
// representation
int I;
public:
// operators
Foo& operator=(const Foo& foo) {
I = foo.I;
std::cout << "Foo:
perator=(const Foo&)" << std::endl;
return *this;
}
friend
std:
stream& operator<<(std:
stream& os, const Foo& foo);
// constructors
Foo(int i = 0): I(i) {
std::cout << "Foo::Foo(int)\tI = " << I << std::endl;
}
Foo(const Foo& foo): I(foo.I) {
std::cout << "Foo::Foo(const Foo&)" << std::endl;
}
~Foo(void) {
std::cout << "Foo::~Foo(void)" << std::endl;
}
};
Foo func(void) {
Foo g;
g = Foo(13);
std::cout << "func(void)" << std::endl;
return g;
}
inline
std:
stream& operator<<(std:
stream& os, const Foo& foo) {
return os << foo.I;
}
int main(int argc, char* argv[]) {
Foo f(func());
std::cout << "f = " << f << std::endl;
return 0;
}
g++ -Wall -ansi -pedantic -o main main.cc
./main
Foo::Foo(int) I = 0
Foo::Foo(int) I = 13
Foo:
perator=(const Foo&)
Foo::~Foo(void)
func(void)
f = 13
I defined a copy constructor but it was never called.
Notice that I modified func(void)
to assign a new value to g before it returns.
Function func(void) initializes object f in function main
directly using the default constructor Foo(int i = 0)
then it constructs a temporary Foo(13)
and assigns this temporary value to object f directly.
Finally, function func(void) destroys the temporary
just before it returns. No destructor is required for g
because g is just another name for the return value
which, in this case, is just object f in function main.