0

When returning a container, I always had to determine if I should use return value or use output parameter. If the performance matter, I chose the second option, otherwise I always chose the first option because it is more intuitive.

Frankly, I personally have been strongly objected about output parameters, possibly because of my mathematical background, but it was kind of OK to use them when I had no other options.

However, the things have been completely changed when it comes to generic programming. There are situations I encountered where a function may not know whether or not the object it returns is a huge container or just a simple value.

Consistently using output parameters may be the solution that I want to know if I can avoid. It is just so awkward if I have to do

int a;
f(a, other_arguments);

compared to

auto a = f(other_arguments);

Furthermore, sometimes the return type of f() has no default constructor. If output parameters are used, there is no graceful way to deal with that case.

I wonder if it is possible to return a "modifier object", a functor taking output parameters to modify them appropriately. (Perhaps this is a kind of lazy evaluation?) Well, returning such objects is not a problem, but the problem is I can't insert an appropriate overload of the assignment operator (or constructor) that takes such an object and triggers it to do its job, when the return type belongs to a library that I can't touch, e.g., std::vector. Of course, conversion operators are not helpful as they have no access to existing resources prepared for the target object.

Some people might ask why not use assign(); define a "generator object" which has begin() & end(), and pass those iterators to std::vector::assign. It is not a solution. For the first reason, the "generator object" does not have the full access to the target object and this may limit what could be done. For the second and more important reason, the call site of my function f() may also be a template which does not know the exact return type of f(), so it cannot determine which of the assignment operator or the assign() member function should be used.

I think that the "modifier object" approach to modify containers should have been already discussed in the past as it is not at all a new idea.

To sum up,

  1. Is it possible to use return values to simulate what would happen when output parameters are used instead, in particular, when outputs are containers?
  2. If not, was adding those supports to the standard discussed before? If it was, what were the issues? Is it a terrible idea?

Edit

The code example I've put above is misleading. The function f() may be used to initialize a local variable, but it may be also used to modify existing variables defined elsewhere. For the first case, as Rakete1111 mentioned, there is no problem with return by value as copy elision comes into play. But for the second case, there may be unnecessary resource releasing/acquiring.

Junekey Jeon
  • 1,496
  • 1
  • 11
  • 18

2 Answers2

1

I don't think your "modifier object" was ever proposed (AFAIK). And it will never go into the standard. Why? Because we already have a way to get rid of the expensive copy, and that is return by value (plus compiler optimizations).

Before C++17, compilers were allowed to do basically almost the same thing. This optimization is known as (N)RVO, which optimizes away the copy when returning a (named) temporary from a function.

auto a = f(other_arguments);

Will not return a temporary, then copy it into a. The compiler will optimize the copy away entirely, it is not needed. In theory, you cannot assume that your compiler supports this, but the three major ones do (clang, gcc, MSVC) so no need to worry - I don't know about ICC and the others, so I can't say.

So, as there is no copy (or move) involved, there is no performance penalty of using return values instead of output parameters (most probably, if for some reason your compiler doesn't support it, you'll get a move most of the time). You should always use return parameters if possible, and only use output parameters or some other technique if you measure that you get significantly better performance otherwise.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • What I'm concerning is not about copy semantics. If f() is going to return a vector, it should construct it first, and that is the part unnecessary if output parameters are used instead. In the above code, a is a local variable constructed from f(), and yes, in this case the behavior is likely (guaranteed, indeed, in C++17?) to be optimal. I'm concerning if it is possible to reuse resources that is already prepared for a if a is a variable defined elsewhere. So my question has little to do with copy elision. – Junekey Jeon Nov 19 '17 at 15:10
  • Imagine a situation when f() is called inside a member function of a class 60 times per every second to modify a member variable. If return values are used, 60 unnecessary calls to deallocate/allocate are made per every second. – Junekey Jeon Nov 19 '17 at 15:18
  • @JunekeyJeon What does `f()` actually do. In your question, you explicitly mention preferring `auto a = f(/*...*/);`, so you aren't modifying anything. Of course, if you want to modify a variable, it is better to pass it by reference, instead of creating a new one and merging (in the case of a vector) it every time. But if you want to create a new object, which I assumed you wanted because of your example, return by value is better. – Rakete1111 Nov 19 '17 at 15:25
  • Sorry if I made you confused. I think I should edit the question accordingly. ````f()```` is some mathematical function that may be applied to certain types. That type will be sometimes integers, sometimes 100x100 matrices, whatever. It is a very generic function that can be used in many different contexts. The context I'm mentioning here is something related to graphic rendering. – Junekey Jeon Nov 19 '17 at 15:30
-2

(Edited, based on comments)

You are right you should avoid output parameters if possible, because the code using them is harder to read and to debug.

Since C++11 we have a feature called move constructors (see reference). You can use std::move on all primitive and STL containers types. It is efficient (question: Efficiency difference between copy and move constructor), because you don't actually copy the values of the variables. Only the pointers are swapped. For your own complex types, you can write your own move constructor. On the other hand, the only thing you can do is to return a reference to a temporary, which is of undefined behavior, for example:

#include <iostream>

int &&add(int initial, int howMany) {
    return std::move(initial + howMany);
}

int main() {
    std::cout << add(5, 2) << std::endl;
    return 0;
}

Its output could be: 7

You could avoid the problem of temporary variables using global or static ones, but it is bad either. @Rakete is right, there is no good way of achieving it.

banan3'14
  • 3,810
  • 3
  • 24
  • 47
  • You don't need the `std::move` in `add`, and in both cases, you are returning a reference to a temporary, which is undefined behavior. – Rakete1111 Nov 19 '17 at 15:03
  • Your code contains flaws. First, you should not return local variables by reference. Second, the vector passed to randomVector is leaked. – Junekey Jeon Nov 19 '17 at 15:04