Consider the following code. What happens when doStuff() is called but the return value is not used? Is SomeClass still created? Of course the creation itself can have important side effects, but so can copy-constructors and they are still omitted in RVO / copy-elision.
SomeClass doStuff(){
//...do stuff
return SomeClass( /**/);
}
SomeClass some_object = doStuff();
doStuff(); //What happens here?
(Edit: tested this with GCC -O3. The object is constructed and then destructed right away)
Returning a Value from a MethodJava requires that a method declare the data type of the value that it returns. If a method does not return a value, it must be declared to return void . Methods can return either values of primitive data types or of reference data types.
A function may be defined to return any type of value, except an array type or a function type; these exclusions must be handled by returning a pointer to the array or function. When a function does not return a value, void is the type specifier in the function declaration and definition.
In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called. In brief, they are Function objects. For more examples and explanations, see the JavaScript guide about functions.
Explanation: This is possible but not always, since the reference being returned may get destroyed with the return of method. This is an undesirable condition, hence it is not always possible to return references. But it is always possible if the referred element is not local to the method.
I feel there's a misunderstanding when it comes to RVO and copy elision. It doesn't mean that a function's return value is not created. It's always created, that's not something an implementation can cop out of doing.
The only leeway, when it comes to eliding copies, despite side effects, is with cutting the middle man. When you initialize an object with the result of the call, then the standard allows plugging the target object in, for the function to initialize directly.
If you don't provide a target object (by using the result), then a temporary must be materialized, and destroyed, as part of the full expression that contains the function call.
So to play a bit with your example:
doStuff(); // An object is created and destroyed as part of temporary materialization
// Depending on the compilers analysis under the as-if rule, there may be
// further optimization which gets rid of it all. But there is an object there
// formally.
std::rand() && (doStuff(), std::rand());
// Depending on the result of std::rand(), this may or may not create an object.
// If the left sub-expression evaluates to a falsy value, no result object is materialized.
// Otherwise, one is materialized before the second call to std::rand() and
// destroyed after it.
A compiler may elide an unnecessary copy in certain cases, even if it has side effects, yes.
A compiler may not elide an object's entire existence, if it has side effects.
If it doesn't have side effects then no result is observable so whether the existence happened or not is effectively a non-question.
tl;dr: the standard lists very specific elision opportunities, and this is not one of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With