1

Possible Duplicate:
In C++, is it still bad practice to return a vector from a function?

In terms of performance, when needing to return'heavier' objects like std::vector or std::string from a function, is it recommended to use this form:

void func(std::vector<int> *dest)
{
}

instead of this form:

std::vector<int> func()
{
    std::vector<int> arr;
    // ...
    return arr;
}

I am assuming that the first form should be faster, but at the same time I've seen the second form often enough, the Qt API often returns a QString for example, probably because it is much more convenient or intuitive to use.

Also I've wondered if there are compiler optimizations which could remove the unnecessary copying of objects when using the return statement.


Edit

Are there any popular compilers still used today which do not perform the optimizations mentioned in the answers?

Community
  • 1
  • 1
sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • Yes, it's called Return Value Optimization. – chris Jan 22 '13 at 18:20
  • 1
    Something to read for you: [Want Speed? Pass by Value](http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/) – ipc Jan 22 '13 at 18:21
  • http://stackoverflow.com/questions/3134831/in-c-is-it-still-bad-practice-to-return-a-vector-from-a-function – K-ballo Jan 22 '13 at 18:22
  • @K-ballo Sorry, didn't find this one, so my question is a duplicate I guess. – sashoalm Jan 22 '13 at 18:27
  • There is plenty of material on the web about this. Search. – Lightness Races in Orbit Jan 22 '13 at 18:29
  • Regarding the edit, a simple check is looking into the calling convention that your compiler uses for returning large objects (larger than a register). All calling conventions I know of translate the return type to a pointer to the location where the returned object is to be built, effectively performing RVO and the same transformation that you meant to do manually – David Rodríguez - dribeas Jan 22 '13 at 18:50

4 Answers4

7

is it recommended to use [pass by pointer] instead of [return by value]?

No.

A modern C++ compiler performs named return value optimisation (NRVO) which effectively means that the compiler reliably elides the copy here. No copy is performed.

Note that this is regardless of which C++ version you are using: C++03 does it as well as C++11. The only thing that changed in C++11 is that the language makes it easier for libraries to make moving out of a value (as is happening here) efficient when no copy elision can be performed.

For return values, copy elision can normally be performed – it’s more relevant in other cases (passing parameters by value, for instance). There are exceptions though; the following code cannot use named return value optimisation. It can use a C++11 move though:

std::string foo() {
    std::string one = "Foo";
    std::string two = "Bar";

    if (rand() % 2 == 0)
        return one;
    else
        return two;
}

The reason is that now two code paths return different named objects; this prevents NRVO.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Since the other answers mention C++11, does this hold before it? Meaning, which is the earliest version of C++ that supports it? – sashoalm Jan 22 '13 at 18:22
  • @satuon It definitely holds before, yes. However, see the caveat in my answer. – Konrad Rudolph Jan 22 '13 at 18:23
  • 1
    @KonradRudolph: But your example *can* use (unnamed) RVO. You'd need an example returning two different named `std::string` variables to defeat the optimisation. – Mike Seymour Jan 22 '13 at 18:26
  • @Mike See updated example, I’ve noticed the same. Hmm, you have a good point, let me think of a better example that cannot be trivially rewritten by the compiler (local variables within the `if` branches don’t cut it directly either). – Konrad Rudolph Jan 22 '13 at 18:29
  • @KonradRudolph Great, I was using pointers till now and it made the code look rather ugly, glad to know I can just return arrays. – sashoalm Jan 22 '13 at 18:33
  • Note on the last part of the answer: The question is about the alternative of either passing by pointer or returning by value. In that context the compiler is **always** able to generate code that is exactly as efficient as the handcrafted *pass-a-pointer* alternative. In the last part of the answer you would have the same problem if you wrote the function to take a pointer. If you can make it more efficient manually, you can perform a simple transformation where the pointer argument is replaced by a local variable that is returned, guaranteeing the same performance. – David Rodríguez - dribeas Jan 22 '13 at 18:53
3

Return by value:

std::vector<int> func();

C++ allows for copy elision for situations just like this, and beyond that the new C++ defined move se­man­tics to make those operations cheap. Compilers generally implement this well. (With copy elision, your local arr will actually end up being constructed right in the recipient call site variable. This situa­tion is also known as "return value optimization".)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

The rules allowing RVO and NRVO were present in the ARM (1990), so it would be surprising if any compiler didn't implement them.

More importantly, using out parameters (either pointers or references to non-const) is extremely unwieldy. Don't do it, until the profiler says that you really are having time problems because of the copying of the return value. At which point, overload the function, along the lines of:

void func( std::vector<int>& dest )
{
    //  ...
}

std::vector<int> func()
{
    std::vector<int> results;
    func( results );
    return results;
}

Then try both, at the location where the profiler says you are having problems, and chose the one which solves the problems (if it makes a difference).

I actually had to do this once, but that was sometime around 1991 or 1992. I haven't had to do it since, and for the last couple of years, I've been working on some pretty performance critical stuff; we still regularly return std::vector or our in house Matrix classes. Without the advantages of C++11, since not all of the targeted compilers support it.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1
std::vector<int> func();

With C++11, the above should be your function. The local std::vector object will be moved when the function returns. If possible, even move can be elided by the compiler.

In short, no worry. Return by value.

Nawaz
  • 353,942
  • 115
  • 666
  • 851