Neal Coombes wrote:
In Herb and Andrei's Coding Standards book Item 25 is about taking
parameters appropriately by value, (smart) pointer, or reference.
There is however no item about how to return.
So If anyone is up for it, I'd be very interested in how to write
Item 25b. Return appropriately by value, (smart) pointer, or
reference.
I'll try to answer the question you've asked in a moment, but I think
the issue is deeper than this.
There are several other ways to get data out of a function. At the very
least, you have to consider exceptions, "out" parameters, and shared
data areas like the state of an object. On top of those, there are more
devious variations on the theme, such as passing in a function object or
using call-backs.
In addition, return values in C++ can provide only a single piece of
information. To return more, we must either use a compound type like a
struct or tuple object, or we must use other mechanisms to supplement or
replace the return value.
In other words, return values do not operate in a vacuum. The best use
for them can depend intimiately on what other approaches are
(potentially) being used as well.
With that caveat out of the way, here are a few guidelines that come to
mind immediately.
1. By default, return by value. This provides natural semantics, and
minimal scope for misunderstand or misuse.
1a. Help your optimiser to make this as efficient as possible. IIRC,
Scott Meyers wrote on this subject in one of his Effective books.
1b. Choose another technique with polymorphic types, to avoid slicing.
1c. Emphasise readability at the point where your function is called.
1ci. If your function's primary purpose is to return some data, name it
after that data.
1cii. Avoid "out" parameters where a return value could be used instead.
It's cumbersome to write this:
SomeType data;
GetDataInto(data);
DoSomethingWith(data);
when you could write this:
DoSomethingWith(Data());
1d. If a returned value is likely to be an intermediate step in a
computation, consider returning placeholder objects and using expression
template techniques to improve effiency.
2. Return a reference where a specific item of internal data should be
directly accessible, as with [] operators for data structures. Also
return a reference from operators that can be chained, such as
assignment operators and insertion/extraction for I/O streams.
2a. Provide versions for const, non-const or both, as appropriate.
2b. Be clear about what conditions invalidate the reference.
3. If you're returning any pointer type, smart or otherwise, be clear
whether a NULL value is possible, and what its significance is if so.
4. Raw pointers are ambiguous about intent, and are best avoided.
5. Where you want to return a pointer to a dynamically allocated object,
or an object of a polymorphic type, use a suitable smart pointer type to
ensure that ownership is transferred or shared as appropriate while
preserving the polymorphic behaviour. At least use an auto_ptr to make
sure ignoring a return value can't create an inadvertent memory leak.
Specify the ownership policy in a comment if it isn't clear from the code.
6. Where you're identifying a particular location in a data structure,
an iterator or some sort of key value may be a more powerful way to
return the information. If a pointer would provide uncontrolled access
to (part of) an array, returning a proxy object to represent that array
slice can provide range-checking for safety, while still offering almost
direct access to the underlying data structure for performance.
I'm sure these could be rearranged into something rather better written
and I'm sure there are omissions, but that's the starting point my
stream of consciousness came up with.
Hope that helps,
Chris