3
votes

As far as I know,in c++17 the concept/semantic of prvalue is no longer temporary object,so in many circumstances the copy elision is mandated.

However, today I came cross a description of return expression

If expression is a prvalue, the object returned by the function is initialized directly by that expression. This does not involve a copy or move constructor when the types match

Why does the term object occurs here? In value category the return of function which is not a reference type belongs to prvalue,so I think maybe it is inappropriate to use the term object.

According to my understanding,the prvalues are no longer objects now,they are just values,am I right?

As a supplement,here also use the term "object".

2
An object and its value type are orthogonal. A prvalue indicates the value type of object. There is still an object involved.R Sahu
@RSahu If that's true,how to comprehend the "prvalue is not temporary object" ?scottxiao
@RSahu What's more,prvalue indicates the value_type of expression,not object.scottxiao

2 Answers

4
votes

I agree with what you say. There is a discussion page on cppreference where you can raise your concerns. A better way to phrase it might be

If expression is a prvalue, the result object of the expression is initialized directly by that expression.

As you say, objects are no longer returned or passed around by prvalues.

2
votes

The quoted text is pointing out that there is an object whose value is set to the return value of the function: with C++17's guaranteed elision when returning by value, that object will be something like a variable being created by the caller, an element in a vector the caller is emplacing or push_backing too, or an object being constructed in some dynamically allocated memory the caller's orchestrated. You're confusing this with a temporary, which as you say might not be involved.

Working through it systematically, you quote cppreference saying of...

return expression;

...that...

If expression is a prvalue, the object returned by the function is initialized directly by that expression. This does not involve a copy or move constructor when the types match

The C++17 Standard says in [basic.lval]:

The result of a prvalue is the value that the expression stores into its context. [ ... ] The result object of a prvalue is the object initialized by the prvalue; a prvalue that is used to compute the value of an operand of an operator or that has type cv void has no result object. [ Note: Except when the prvalue is the operand of a decltype-specifier, a prvalue of class or array type always has a result object. For a discarded prvalue, a temporary object is materialized; ...

So, in the cppreference text, what the standard terms the "result object" is being referred to as "the object returned by the function". The language is a little bit imprecise in saying the result object is "returned" rather than "initialised", but overall it's not misleading and - for having avoided yet another bit of terminology - may well be easier for most cppreference readers to understand. I'm not actively involved with the site, but as a regular user my impression is that cppreference is trying to accurately explain the essence of the standard but simplifying language a smidge when possible.


While the underlying mechanisms are left unspecified by the Standard - and the practicalities of optimisation / ABIs dictate different implementation - to get a sense of what the Standard requires happen functionally it might help to imagine the compiler implementing code like...

My_Object function() { return { a, b, c }; }
...
... {
    My_Object my_object = function();  // caller
}

...by secretly passing a memory-for-returned-object address to the function (much like the this pointer to member functions)...

void function(My_Object* p_returned_object) {
    new (p_returned_object) My_Object{ a, b, c };
}

So, there is an object involved and constructed by the called function, but its whereabouts is elided - i.e. a caller-specified memory address. If the function call result is unused by the caller, a temporary is at least notionally constructed then destructed.