
Pierre Morcello wrote:
Hi Luke,
lucanus.j.simonson@intel.com wrote:
"To return a container pass it by non-const reference and populate it, which avoids copy on return."
Certainly, but shouldn't we promote a little more copy elision instead ? This is supposed to avoid copy on return too, or did I misunderstood it? http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
Best regards,
Pierre
PS: grats for the polygon lib!
Thanks.
Peter Foelsche wrote: One should also be cognizant of the optimization opportunities afforded you by the presence of Return Value Optimization (RVO) and move semantics (if available). These generally give one the same interface choices that copy-on-write would allow but without the overhead. For example, if your function does an unconditional copy-modify-return, you should pass by value and return by value; contemporary compilers elide the spurious copies.
To specifically address returning a container: If I wanted to return a container, I think you want to prefer returning by value and relying on RVO and/or move assignment and/or swap. This simplifies the interface and makes the program's reasoning easier to parse. I would only modify an existing container in-place (i.e., take a reference to a to-be-modified container) if the semantics are explicitly to "add" to the container.
Of course, rvalue references aren't available on every compiler yet. For those C++03 compilers, one could use the (proposed) Boost.Move library or, if that isn't an option, swap can sometimes be used as poor man's move assignment.
Peter and Pierre have some points about the direction C++ is taking from the old standard usage and the new. I actually like to have an accumulate semantic on containers I pass by reference most of the time. I know I should use an output iterator instead, mea-culpa. The other thing I like about pass by reference is it places memory management responsibility on the caller (which output iterators also do, of course). If the caller wants to allocate a std container on the heap or make it a data member of a class and pass it to a function by reference to populate it they can do so at no extra cost. Yes r-value references, NRVO, RVO and copy ellison/move semantics all help make those things zero-cost, but I take some comfort in writing code that I know for sure will compile to an efficient executable in every C++ compiler. Also, I don't find passing by const reference to be much of a cognitive overhead and I admit passing by reference to populate a container gets in the way of equational reasoning and forces multi-line syntax where a single expression might be more natural, but I like accumulate semantics and I like pushing ownership of containers down the call stack as far as possible and not trasfering that ownership around. Generally when passing containers around I like to make them template parameters. A return value type can't be an implicit template parameter, while calls to a template function with pass by reference can usually be made bare without supplying a template parameter list, which makes using them more intuitive. We can never infer the return type of a template function by what comes on the left hand side of the assignment operator. Well, I guess I do use expression templates with generic assignment operators to get my equational reasoning, infer the right hand side and still use pass by reference under the hood. It's a lot of work, but we have proto to help with that. Getting the ownership of an object right in the first place seems to me to be 95% of the problem and a lot of these things like RVO and copy-ellison is there to address the 5%. I'd rather people learn how to manage the lifetime of their objects properly than learn the new language features. Once you have the 95% covered see how the new langauge features can help you with the remaining 5%. Build the pyramid from the bottom up. Regards, Luke