Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding copy of objects with the "return" statement

I have a very basic question in C++. How to avoid copy when returning an object ?

Here is an example :

std::vector<unsigned int> test(const unsigned int n) {     std::vector<unsigned int> x;     for (unsigned int i = 0; i < n; ++i) {         x.push_back(i);     }     return x; } 

As I understand how C++ works, this function will create 2 vectors : the local one (x), and the copy of x which will be returned. Is there a way to avoid the copy ? (and I don't want to return a pointer to an object, but the object itself)


What would be the syntax of that function using "move semantics" (which was stated in the comments)?

like image 763
Vincent Avatar asked May 07 '12 04:05

Vincent


People also ask

Are return values copied?

If a function returns a reference, and that reference is used to initialize or assign to a non-reference variable, the return value will be copied (as if it had been returned by value).

Does returning an object copy it C++?

Returning an object invokes the copy constructor while returning a reference doesn't. So, the version #2 does less work and is more efficient. The reference should be to an object that exists when the calling function is execution.

Which functions should not use a return statement?

In lieu of a data type, void functions use the keyword "void." A void function performs a task, and then control returns back to the caller--but, it does not return a value. You may or may not use the return statement, as there is no return value.

What does it mean to return a reference to an object?

It means you return by reference, which is, at least in this case, probably not desired. It basically means the returned value is an alias to whatever you returned from the function. Unless it's a persistent object it's illegal.


2 Answers

There seems to be some confusion as to how the RVO (Return Value Optimization) works.

A simple example:

#include <iostream>  struct A {     int a;     int b;     int c;     int d; };  A create(int i) {     A a = {i, i+1, i+2, i+3 };     std::cout << &a << "\n";     return a; }  int main(int argc, char*[]) {     A a = create(argc);     std::cout << &a << "\n"; } 

And its output at ideone:

0xbf928684 0xbf928684 

Surprising ?

Actually, that is the effect of RVO: the object to be returned is constructed directly in place in the caller.

How ?

Traditionally, the caller (main here) will reserve some space on the stack for the return value: the return slot; the callee (create here) is passed (somehow) the address of the return slot to copy its return value into. The callee then allocate its own space for the local variable in which it builds the result, like for any other local variable, and then copies it into the return slot upon the return statement.

RVO is triggered when the compiler deduces from the code that the variable can be constructed directly into the return slot with equivalent semantics (the as-if rule).

Note that this is such a common optimization that it is explicitly white-listed by the Standard and the compiler does not have to worry about possible side-effects of the copy (or move) constructor.

When ?

The compiler is most likely to use simple rules, such as:

// 1. works A unnamed() { return {1, 2, 3, 4}; }  // 2. works A unique_named() {     A a = {1, 2, 3, 4};     return a; }  // 3. works A mixed_unnamed_named(bool b) {     if (b) { return {1, 2, 3, 4}; }      A a = {1, 2, 3, 4};     return a; }  // 4. does not work A mixed_named_unnamed(bool b) {     A a = {1, 2, 3, 4};      if (b) { return {4, 3, 2, 1}; }      return a; } 

In the latter case (4), the optimization cannot be applied when A is returned because the compiler cannot build a in the return slot, as it may need it for something else (depending on the boolean condition b).

A simple rule of thumb is thus that:

RVO should be applied if no other candidate for the return slot has been declared prior to the return statement.

like image 179
Matthieu M. Avatar answered Sep 22 '22 19:09

Matthieu M.


This program can take advantage of named return value optimization (NRVO). See here: http://en.wikipedia.org/wiki/Copy_elision

In C++11 there are move constructors and assignment which are also cheap. You can read a tutorial here: http://thbecker.net/articles/rvalue_references/section_01.html

like image 32
Pubby Avatar answered Sep 25 '22 19:09

Pubby