1

Consider that TEST code:

#include <iostream>
using namespace std;

class Klass
{
public:
  Klass()
  {
    cout << "Klass()" << endl;
  }

  Klass(const Klass& right)
  {
    cout << "Klass(const Klass& right)" << endl;
  }
};

Klass create(Klass a)
{
  cout << "create(Klass a)" << endl;
  return a;
}

int main()
{
  const Klass result = create(Klass());
}

Compiling with:

g++ -O3 rvo.cpp   -o rvo

The output is:

$ ./rvo
Klass()
create(Klass a)
Klass(const Klass& right)

I was expecting the compiler to use the RVO mechanism in order elide every COPY CTOR call, to avoid copying the return value AND the parameter of the function create(). Why isn't it the case?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
yves Baumes
  • 8,836
  • 7
  • 45
  • 74
  • 6
    Why should *return* value optimization kick in for *passing* an argument to a function? – PlasmaHH Feb 15 '12 at 12:52
  • I always heard the name is 'abusively' used to refer to that mechanism: avoid copy construction. – yves Baumes Feb 15 '12 at 12:58
  • And obvisouly here, following the ouput trace, the returned value is copied not the parameter. – yves Baumes Feb 15 '12 at 13:00
  • RVO is in the standard. Isn't it ? – yves Baumes Feb 15 '12 at 13:01
  • 1
    @PlasmaHH: RVO is a poor name; copy elision rules aren't restricted to `return`. Take a look in the standard. – Lightness Races in Orbit Feb 15 '12 at 13:03
  • 3
    @LightnessRacesinOrbit: It is a poor name when used to mean other things too, but it is a good name when you mean return value optimization. Just like STL is a poor name when you mean the stdlib, but it is a good name when you mean the STL. RVO will of course not work for parameters, but that does not mean there are other mechanisms that may elide copies too. Ultimately the as-if rule allows many things to happen. – PlasmaHH Feb 15 '12 at 13:07
  • @PlasmaHH: So "RVO" should only be used to talk about the limited subset of the copy elision rule that talks about returning values? OK, but it seems clear that the OP is talking about elision in general. – Lightness Races in Orbit Feb 15 '12 at 13:08
  • 2
    @LightnessRacesinOrbit: Well, it wasn't clear to at least me, since when something has "return value" in its name, it is clear to me that its scope is reduced to return values. Hence the question why he expected it to kick in for passing parameters too. – PlasmaHH Feb 15 '12 at 13:11
  • @PlasmaHH: I'm sure, then, it's ironic that it _does_ kick in for passing parameters, but _not_ for the return value. Satisfied? – Lightness Races in Orbit Feb 15 '12 at 13:13
  • @LightnessRacesinOrbit: Indeed, and it would maybe be worthwile to put this as a kind of bugreport to the gcc people about a possible missed optimization opportunity, since I think it could be useful even for less contrived situations. – PlasmaHH Feb 15 '12 at 13:26
  • 1
    @PlasmaHH: How so? It's prohibited by the standard, as both answers to this question attest. – Lightness Races in Orbit Feb 15 '12 at 13:38

2 Answers2

3

The standard allows copy elision only in case where you pass a temporary as a function argument.

The two elisions you're expecting are bolded below:

[C++11: 12.8/31]: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
  • in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
  • when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
  • when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration. [..]

It didn't happen for the return value because the non-volatile name was a function parameter.

It has happened for the construction into create's parameter, otherwise you'd have seen:

Klass()
Klass(const Klass& right)
create(Klass a)
Klass(const Klass& right)
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Great answer (+1), but even g++ does have a manual. Or reason of g++ behavior may be explain somewhere in a mailing list by its developer. That is just what I am asking for here. – yves Baumes Feb 15 '12 at 13:11
  • I did not downvote, but the answer is wrong, the standard does allow the optimization with input arguments and return values, but explicitly disallows it for returning an input parameter. – David Rodríguez - dribeas Feb 15 '12 at 13:13
  • @yves: Edited. One of the elisions is prohibited. – Lightness Races in Orbit Feb 15 '12 at 13:14
  • 1
    @DavidRodríguez-dribeas: Yes, I edited that into my post several minutes ago. That's why I asked downvoters to review. But it seems they just keep on coming without bothering to refresh. – Lightness Races in Orbit Feb 15 '12 at 13:14
  • Ok , thank every one. Seen the: "other than a function or catch-clause parameter" explaing it all. Just wonder why this limitation now .. But it's another question! :-) – yves Baumes Feb 15 '12 at 13:16
  • @yves: You definitely won't get an answer to that one :P – Lightness Races in Orbit Feb 15 '12 at 13:18
  • @yves An attempt to explain is (shameless plug) in my answer. Most such limitations have reasons when you think about them in terms of implementation. – Suma Feb 15 '12 at 13:20
  • 3
    @yvesBaumes: You will get an answer, and it is actually quite simple, in most (all I know) cases the calling conventions determine that the caller is responsible for allocating and initializaing the arguments to the function **and** also allocate the space for the return statement, passing a pointer to the function. Copy elision is implemented by creating both the original and the (not) copied objects in the same memory location (both are a single object), but in this case the caller which is constructing the argument cannot know (inlines aside) that the parameter will be returned. – David Rodríguez - dribeas Feb 15 '12 at 13:23
  • 1
    ... more on that [here](http://definedbehavior.blogspot.com/2011/08/value-semantics-copy-elision.html) (I also wrote a couple other posts on value semantics that might be of interest. Then my life turned upside down and stopped writing :) – David Rodríguez - dribeas Feb 15 '12 at 13:26
  • @DavidRodríguez-dribeas I will check that out. Thnx. – yves Baumes Feb 15 '12 at 13:35
  • 1
    Ok I've just modified my code and inline the create() function. Same output as without. It's very likely that g++ is just following what the c++ standard request to do. – yves Baumes Feb 15 '12 at 13:42
  • 1
    @yvesBaumes: `inline` as a keyword does not exactly mean *inline*, the compiler is free to inline or not regardless of the `inline` keyword. Try compiling with -O3 or whatever the highest optimization level is in your compiler and check if it changes anything, and also note that the lack of visibility into the function is a reason not to perform the optimization, but the availability of visibility is not a reason to actually *get* that optimization. In C++11 the standard still requires that the copy is not elided (but the object contents can be moved, which is the next best thing) – David Rodríguez - dribeas Feb 15 '12 at 14:05
2

The copy you see is a copy for the "return" statement in the "create" function. It cannot be eliminated by RVO, as it is not possible to construct the return value directly. You requested to "return a". A copy is needed here; there is no way to return an object without it.

In a standard speak, following condition of [C++11: 12.8/31] is not met

in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value

As for the reasons, it is not an arbitrary rule, it makes sense from implementation point of view, as this is what is not possible to do with a function parameters:

constructing the automatic object directly into the function’s return value

You are copying the function parameter. You cannot elide this copy without inlining, as the parameter already exists before you enter the function, therefore you cannot construct that object into the return value directly instead.

Suma
  • 33,181
  • 16
  • 123
  • 191
  • This answer isn't quite right. You're quoting the wrong passage. The real problem is that the "non-volatile automatic object" is a function parameter. – Lightness Races in Orbit Feb 15 '12 at 13:12
  • `this is what is not possible: by constructing the automatic object directly into the function’s return value` rules are allowed to be combined. – Lightness Races in Orbit Feb 15 '12 at 13:19
  • I quoted the _entire_ passage and bolded the relevant parts. You chose a _non_-relevant part of it to quote. – Lightness Races in Orbit Feb 15 '12 at 13:19
  • @LightnessRacesinOrbit You are correct. Sorry for my mistake. – Suma Feb 15 '12 at 13:22
  • @LightnessRacesinOrbit "rules are allowed to be combined" this is not about rules this time, this is about practical impossibility. You cannot elide this copy without inlining, as the parameter already exists before you enter the function. – Suma Feb 15 '12 at 13:23
  • BTW, downvote removed now that you fixed the quotes :) I think that this answer is comprehensive now, and certainly would be optimally so if you placed your last comment into it. – Lightness Races in Orbit Feb 15 '12 at 13:37