Return Statement: does it have a PlaceHolder?

Jun 2, 2016 at 9:56pm
Hello.

I am coming back from this question I posted on StackOverflow: http://stackoverflow.com/questions/37594496/is-move-constructor-called-twice-in-c/37594599

If you see from the answers, the temporary object is first moved into the return value placeholder and THEN it's moved into the instance myObject.

I really need some more explanations about it: what is the return placeholder? Wtf? I never knew that!

Last edited on Jun 2, 2016 at 10:00pm
Jun 2, 2016 at 10:00pm
it's that space in the C calling convention, on stack or in CPU registers, that the caller prepares before initiating the function call, and where it finds the result of the function call when control returns to it.
In standardese terms, a function call to a function returning by value is an rvalue expression; that "placeholder" is where the value of that expression is located.
Jun 2, 2016 at 10:08pm
Before the copy elision was introduced, did the compiler really use to:

1. Construct the temporary
2. Move the temporary into the return placeholder
3. Destruct the temporary
4. Move the return placeholder inside the pending lvalue object

That was lots of work.

Also, I bolded "Move" because I need to know: is the temporary always MOVED into the return placeholder?

I'm testing some cases, and it doesn't seem to be ever copied by copy constructor, only moved...
Last edited on Jun 2, 2016 at 10:10pm
Jun 2, 2016 at 10:16pm
There is no such thing as "lvalue object". Execution of what program are you describing?

move from the return statement's expression into the return placeholder of the function was introduced in C++11, since it involves overload resolution between various move constructors and binding of an rvalue reference.

copy elision was introduced around 1992
Jun 2, 2016 at 10:20pm
There is no such thing as "lvalue object". Execution of what program are you describing?


The program is always the same, the function f() i'm talking about is this:

1
2
3
4
5
Foo f()
{
    Foo cubbi(" Hey man, thanks for answering my question on Quora yesterday =) ");
    return cubbi;
}


This function

1) Create the temporary cubbi
2) Moves cubbi into the return placeholder
3) Destructs cubbi
4) Moves the return placeholder to myObject (which is the lvalue object i was talking about earlier)

And temporaries (such as cubbi) are always treated as rvalues and hence, moved into the return placeholder, never copied. Correct?
Last edited on Jun 2, 2016 at 10:24pm
Jun 2, 2016 at 10:36pm
There is no such thing as "lvalue object". The only temporary here is result of the expression f() (when copy elision is disabled).

For reference, I suppose "myObject" refers to this line from the linked stackoverflow Q&A

1
2
3
4
int main()
{
    Foo myObject = f();
}


In post-1992 C++ the following happens:

1) The declaration at line 3 of f() creates a local variable of type Foo on the stack frame of the main function. It is accessible under the name 'cubbi' within the scope of f() and under the name 'myObject' within the scope of main.

If you were to disable all copy elisions but use C++11 (so there is such a thing as move), the following would happen:

1) The declaration at line 3 of f() creates a local variable cubbi of type Foo on the stack frame of f().
2) The return statement looks up constructors of Foo that would take an rvalue Foo as an argument. Foo(Foo&&) is a match, and that constructor is called to initialize the Foo temporary that is the result of f() (that "placeholder" you're asking about), with cubbi (treated as rvalue so it binds) as the argument.
3) the destructor of cubbi runs as f()'s stack frame is undone
4) the main function now looks up constructors of Foo that would take an rvalue Foo as an argument. Foo(Foo&&) is a match, and that constructor is called to initialize myObject, which is a local variable on the stack frame of the main function
5) the destructor of the Foo temporary (originally created by f()'s return statement and gutted by mObject's move constructor) runs

(note, your stackoverflow question was concerning a different f(), one that executed "return Foo("Hello");")
Last edited on Jun 2, 2016 at 10:42pm
Jun 2, 2016 at 10:53pm
Ok thanks for being exhaustive. Now I sum up to see If I understood correctly:

1) The function f() creates a temporary [cubbi]

2) The return placeholder is filled/initialized/constructed by move-constructing cubbi

3) cubbi is useless now, let's get rid of him -> Destructor (oh not so useless, thank you for your time ;) )

4) Now myObject inside the main() is still waiting for this hellish operation to come out. Good news for it: we are now providing it a brand new rvalue. So myObject is finally move-constructed with the return placeholder

5) The return placeholder is now moved, so it can be destructed()

6) myObject gets out of scope -> destructed

I hope I got it

question:

The return statement looks up constructors of Foo that would take an rvalue Foo as an argument


always rvalue?
Last edited on Jun 2, 2016 at 10:55pm
Jun 2, 2016 at 10:57pm
okay, nothing seems wrong except terminology in "1)": [cubbi] is not called a "temporary", it is called an "object with automatic storage duration", or "local variable".
The difference is that destructors of temporaries run at the next semicolon (unless extended), while destructors of local variables run at function exit.
In this example, "cubbi" and "myObject" are local variables whose destructors run at the end of f() and main() respectively, while the object returned from f() is a temporary and its destructor runs at the semicolon of the line "Foo myObject = f();"
Jun 2, 2016 at 11:00pm
Very very clear man, thanks =)
Topic archived. No new replies allowed.