darklord
darklord

Reputation: 5167

Why function in c++ can return stack values

Consider the following program:

class C {
  ...
};

const C f() {
    C ret;
    cout << &ret << endl;
    return ret;
}

int main() {
    C value = f();
    cout << &value << endl;
}


result: // note the address are the same
0x7ffdd24b26e0
0x7ffdd24b26e0

The variable 'ret' in function f() and variable 'value' has the same memory address so it seems 'value' is not a copy of 'ret'. The variable 'ret' is a stack variable so it should be invalidated after f() returns. So why c++ allow returning a stack value inside a function?

g++ version:

g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4

Upvotes: 1

Views: 103

Answers (4)

MSalters
MSalters

Reputation: 180265

I'm not sure whether this is actually RVO. An equally valid hypothesis is that the storage of both ret and value is on the implementation's stack, and that they share the same stack slot because their lifetimes are effectively non-overlapping. There might have been an intermediary.

Upvotes: 0

MikeMB
MikeMB

Reputation: 21166

Returning the function result by value is one of the places, where the compiler is allowed to elide a copy as an optimization and thus translates your code to the moral equivalent of this :

void f(uint8_t* memory) {
    new(memory) C; // create object reserved memory location
    cout << (size_t)memory << endl; 
}

int main() {
    alignas(alignof(C)) uint8_t value[sizeof(C)]; //<- reserve properly aligned raw storage of appropriate size on the stack
    f(value);
    cout << (size_t)&value[0] << endl;
}

This optimization technique is called NRVO (named return value optimization) and is actually a pretty natural consequence of most calling conventions which specify that - for values that can't be returned via a register - the returned value is put at an address that is specified by the caller anyway.

Upvotes: 1

Spacemoose
Spacemoose

Reputation: 4016

This is an example of copy-elision, specifically RVO (return value optimization). It allows you to avoid the performance penalty of returning an object.

I've worked with a lot of people who were unaware of RVO and wrote stuff like:

void get_stuff(std::vector<int>& foo /*and input params*/) {
    // add a whole lot of integers into foo.
}

because they thought this would be cheaper (by avoiding a copy) than:

void get_stuff(/*input params*/){
      std::vector foo;
      // populate foo.
      return foo;
}

This leads to unnecessarily verbose, and often difficult to read code. It's quintessential premature optimization -- a mistake you won't be making, since you now know about RVO!

Upvotes: 3

Johan Lundberg
Johan Lundberg

Reputation: 27068

The reason for the address of ret and value being the same is the so called Return Value Optimization (RVO). It means that in this case a copy is not going to be performed. Note however, that you can not rely on this, as it is not bound to happen (although that will change with C++17 [at least the current draft]).

https://en.wikipedia.org/wiki/Return_value_optimization

Upvotes: 4

Related Questions