* (e-mail address removed):
In general one should not optimize prematurely. However, designing a
program so that it needlessly copies megabytes of data in function
calls is ungood. The advice about optimization is really about
micro-optimization, not about not applying common sense.
Yes. In my defense, I did not do something as dumb as passing
megabytes of data by value instead of reference. The values passed
around are just one integer and a two integer struct, as in my
example. Google led me to believe that there is not much speed
difference in passing by reference instead of value for such small
data -- sometimes (for some compilers) passing by value may be faster,
Google said.
For a large vector or something like that, the simplest technique is
to provide 2 variants of your function:
void getDataset( std::vector<Whatever>& result )
{
std::vector<Whatever> tempResult;
// Fill in tempResult here, then:
tempResult.swap( result );
}
inline std::vector<Whatever> dataset()
{
std::vector<Whatever> result;
getDataset( result );
return result;
}
Now users can just use the simple function call notation for
dataset(), until they discover that hey, this is a performance
bottleneck, at which time they measure, find that it's your dataset()
that's doing a lot of copying, and then switch to using getDataset().
That's a useful technique to know.
But that may in reality never occur, because a modern compiler is
likely to do Return Value Optimization, where a call of dataset()
produces machine code almost like it was getDataset that was called
directly -- the "almost" is because RVO is more efficient than that.
I just found out about (N)RVO today, in this thread. From a quick
search it seems that my compiler (GNU GCC) does (N)RVO, so there was
little reason for my initial worry.
But, wow, did I get more than I bargained for bellow! : )
If one is pessimistic enough to believe that (1) the user's compiler
won't do RVO, and (2) the conditions for the performance problem
(enough data, called repeatedly) will be present, and (3) clean
notation like the dataset() function is required, then the only
recourse is to implement something like RVO manually. Usually that's
called "move semantics". For example, Andrei Alexandrescu has
implemented a std::vector replacement with move semantics (part of his
YASLI which it seems will never be completed), plus a general
framework for move semantics called Mojo. As another example, a
number of people were involved in implementing two smart pointers
called move_ptr for a Boost proposal; it was not accepted but still
the code could help. As a third example, Howard Hinnant has
implemented an emulation of C++0x unique_ptr (however, older compilers
such as MSVC 7.1 don't like that code).
But if you don't want that new-fangled stuff, then there's nothing
very wrong in this context with just using std::auto_ptr, which is a
smart pointer with ownership transfer, almost like move semantics.
That usage can, if needed, be wrapped in a little class. The downside
is that using a smart pointer as result type is that (A) the notation
is a little bit less clean (after all, it's a pointer), and (B) it
involves an additional dynamic allocation and indirection.
Wow, thanks for that. It will take me a while to digest it.
Fortunately, my problem is simple enough that I do not need to do RVO
manually. But I might need it for other parts of my program, so I've
saved your text for future reference.
Thank you!
Alex