10

lets say I have the functions

std::Vector<Point> calculate() {
   std::Vector<Point> points; //do stuff with points
   return points;
}

and

void calculate(std::Vector<Point>& points) {
   //do stuff with points
}

So my question is specific to objects initialized on the stack, and are stl objects. Is there any difference in performance, and what is the popular method of doing it

regards

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Moataz Elmasry
  • 2,509
  • 4
  • 27
  • 37
  • 1
    depends on compiler optimizations and a few other things. – Wug Aug 21 '12 at 14:18
  • 3
    Can't you try to benchmark this and make some deductions by yourself ? – undu Aug 21 '12 at 14:20
  • @undu of course I can, I'd like to know what others are prefering, and just because one method yields good results on my compiler and environment does not make a rule that a certain method is better – Moataz Elmasry Aug 21 '12 at 14:23
  • See [this related post](http://stackoverflow.com/questions/10553091/what-is-the-best-way-to-return-string-in-c). – juanchopanza Aug 21 '12 at 14:25
  • 1
    @MoatazElmasry No, please don't benchmark, at least not before using your (and the other answerers') simple reasoning. – Christian Rau Aug 21 '12 at 14:27

4 Answers4

16

Taking the value as a reference parameter has the following properties:

  1. No copying, moving, or any other operation will be done.
  2. The return value cannot be immediately discarded on the user's side. They can't just shove a temporary at your function's reference parameter or something. They must declare a variable, and therefore they must give it a name which will live within the current scope.
  3. The API suggests that the value is an input/output parameter. That is, there is a value being passed in which will be read and written. If that is not the case, then using it represents a sub-optimal API design element.

Returning the value has the following properties:

  1. If copy elision is not available (either due to the nature of the function's implementation, a poor compiler, or that the return value is not initializing a new value), then the return value will be moved. Not copied. Movement is not free, but generally it is not much more expensive than a few pointer copies. No new objects or memory will be allocated or deallocated.
  2. The API enforces the output nature of the value. There is no way for the user to play with the output it passes in because it doesn't pass anything in. Similarly, there is no way for the function to read any values because it doesn't take anything in. It is an output value, period; the function generates it and returns it.
  3. The return value can be discarded immediately at the user's discretion. Obviously if users are doing this a lot, it suggests that something is wrong, but it is up to the user to decide if they want to keep the output value or not. The C++17 [[nodiscard]] attribute can be used in cases where discarding the value is categorically incorrect.
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
10

The performance will very likely be the same, because of copy elision.

What the two methods express is different

std::vector<Point> calculate()

returns a vector (probably based on some parameters).

void calculate(std::vector<Point>& points)

modifies an existing vector (again, probably based on parameters).

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • no modifications, lets say its just an output parameter not input/output – Moataz Elmasry Aug 21 '12 at 14:19
  • 2
    @MoatazElmasry if it's output, just go with return value. It's more logical IMO. – Luchian Grigore Aug 21 '12 at 14:20
  • 2
    @MoatazElmasry That's the point of his answer. A reference parameter suggests input/output and has therefore a different semantic than a return value. – Christian Rau Aug 21 '12 at 14:21
  • 2
    @MoatazElmasry then the second option will be vastly inferior. For example, you will have to document what happens if the input vector is not empty, perform some checks on it and so on. – juanchopanza Aug 21 '12 at 14:22
  • 4
    Besides that C++11 finally allows you to count on this performance equivalence as guaranteed (at least for `std` containers and other well-written lightweight wrappers) and relieves you of the burden of trusting your compiler's (maybe usual but still implementation-defined) optimization facilities. – Christian Rau Aug 21 '12 at 14:23
1

My humble opinion: big return values create more problems than solve... they look simpler, they are more concise in the calling segment, but rvalue references would be avoided.

If you look at old, but designed for efficiency software packages like BLAS and LAPACK, big objects are passed by reference and return values are just error flags or doubles (ddot for example in BLAS).

The shortcoming is that expressiveness will be reduced, but that is a small cost, since resources like memory allocation will move upstream.

C++ can be significantly simplified in terms of syntax and resource management. Passing non-const references has a cost in terms of "prettiness" and code length but it's efficient.

  • 1
    Can you edit your answer to be more specific? What problems do return values create? – Josef Bláha Dec 13 '20 at 09:40
  • Hello, welcome to Stack Overflow. It would make for a more informative answer if you'd elaborate as to why rvalue references should be avoided. – Giogre Dec 13 '20 at 13:46
-1

In Google coding conventions, they recommend using return value rather than output parameter. It's because return value improve readability and often provide the same or better performance.

For readability, I think It's obvious. With output parameter, you have to look over all parameter and determine which ones use for output parameter.

For performance, Scot Meyer said 'Use const whenever possible' in his book. We can't use const for output parameter. Moreover, modern compilers use return value optimization (RVO) or named return value optimization (NRVO) to optimize away the copy operation.

Read these for more details: [1][2]

user16217248
  • 3,119
  • 19
  • 19
  • 37