Stroustrup indicates that returning by value can be faster than
returning by reference but gives no details as to the size of the
returned object up to which this holds.
My question is up to which size m would you expect
vector<double> returns_by_value()
{
int m=...;
vector<double> x(m);
return x;
}
First, the efficiency of this function depends on the compiler. Some
perform what is called named return value optimization (NRVO), which
is related to the fact that your returned value has a name ("x").
However, many more compilers support unnamed return value optimization
(RVO), which has the additional requirement that the returned object
be an unnamed temporary:
vector<double> returns_by_value () {
int m=...;
to be faster than
vector<double>& returns_by_reference()
{
int m=...;
vector<double>* x=new vector<double>(m);
return *x;
}
Never return a reference to a dynamically-allocated object which the
caller is expected to clean up, not even as an example. You should
return an object by reference only if that object exists before the
function call, i.e., if it's global, static, or passed in by pointer
or reference (including the "this" pointer for member functions).
This is important, because your question is purely academic (read:
pointless) if you're not going to use an example of code you might
reasonably use in an actual program. In an actual program, the obvious
alternative to returning by value is to take a pointer or reference to
the object and modify it in-place, like so:
vector<double> &assigns_by_reference (vector<double> &x) {
int m=...;
x.assign(m, 0.0);
return x;
}
It's only fair to compare this real-world code to returns_by_value();
that is what I will do.
So, assigns_by_reference() requires that its argument be constructed
already, whereas returns_by_value() does not:
// (a)
vector<double> foo; // default-construct
assigns_by_reference(foo); // assign new value
// (b)
vector<double> bar = returns_by_value(); // construct w/ new value
If the compiler supports [N]RVO, (b) may actually be negligibly faster
than (a). [N]RVO allows the compiler to construct the returned value
*inside* "bar", thus eliminating (we call it "eliding") a copy
constructor or destructor call, as well as any code dealing with
returning a value (since the returned value is used directly as
"bar").
HOWEVER....
If the compiler doesn't support [N]RVO, then (b) may involve an
additional copy construction and destruction of the temporary. The
same is true if the code involves assignment, as [N]RVO applies only
to construction:
// (c)
vector<double> bar; // default-construct
bar = returns_by_value(); // assign new value
As you guessed, exactly how slow this is depends on the size of the
returned vector. Unfortunately, the actual "m" at which (b) (without
[N]RVO) or (c) becomes slower than (a) is dependent on the compiler.
The only real way to find out is to run it with various values for "m"
and see what happens.
Note: You can replace (c) with this:
// (d)
vector<double> foo; // default-construct
returns_by_value().swap(foo); // swap old and new values
This should be roughly the same as (a), and about as fast, too.
Hope that helps!
- Shane