Search code examples
c++pointersreferencepass-by-referencepass-by-pointer

Function return value and assignment in C++


In C++ or for any other language, I wish to know that if a function returns a local variable in it's scope to a caller and assigns it to some other variable, how do the semantics work? An example in C++ which I have tried is as follows:

Code:

int my(){
    int t=10;
    cout<<&t<<"\n";
    return t;
}
int main() {
    const int& c=my();
    cout<<&c;
    return 0;
}

Output:

0x7fff71249164
0x7fff71249184

In the above example, I think I am passing it by reference so shouldn't the outputs be same? But I just happened to obtain different outputs. Also, I tried changing the return type of my() from int to int& but still got different outputs. I also tried passing using pointers, there too the outputs come out to be different. I just happened to study C++ recently and am getting confused, so any help would be greatly appreciated. Platform used: ideone online C++ (gcc 8.3)

I was expecting that for all my experiments, I would get equal values printed and the addresses would be the same. But is there something I am missing?


Solution

  • my returns by-value, not by-reference, as specified by the return type int instead of int&. (And if you did return-by-reference, then you would have undefined behavior because the reference would be dangling after the call to my, because t lives only until then.)

    A function call expression which calls a function that has a non-reference return type, such as my(), is a prvalue expression. (The same as kind of expression as e.g. an integer literal expression 42.)

    When you initialize a const lvalue reference variable (e.g. c) with a prvalue expression, a temporary object is materialized from the prvalue and the reference is bound to that temporary object. The temporary object is initialized with the value of the prvalue expression and given an (extended) lifetime equal to that of the reference, i.e. the temporary object lives until the end of the scope of the reference.

    This temporary object is generally a distinct object from the object belonging to the t variable. Because they are different objects, they do not need to have the same address. On the other hand, because the lifetime of t ends when the function my returns and the lifetime of the temporary object begins only after it has been initialized, their lifetimes do no overlap. Therefore the compiler would also be free reuse the memory of t for the temporary object so that the addresses would be the same. (The details are actually more complicated, but the end result the same for trivial types such as int. For non-trivial types the compiler may not be allowed to reuse memory in this situation, although the following paragraph still can have the same effect.)

    Furthermore, a compiler is allowed to apply the so-called named return value optimization (NRVO), which allows the compiler to treat the temporary object and t as referring to the same object, thereby skipping any copies that would otherwise be necessary. In that case the addresses can also be the same. Compilers are not going to do this for type int, but may do so for more complex types.


    All of the above applies only to C++. For example in C, the rules are different.