Does return value optimization work when assigning a different type?

Consider the following two classes:

class Base { Base(const Base& other) {...} // relatively expensive operations here... Base(int i) {...} // ...here, virtual ~Base() {...} // ...and here ... }; class Derived : public Base { ... Derived(const Base& other) :Base(other) {...} // some typechecking in here virtual ~Derived() {} ... }; 

This means that Base can be "upcast" with the second Derived constructor. Now consider the following code:

 Base getBase() { int id = ... return Base(id); } ... int main() { Base b = getBase(); // CASE 1 Derived d1(b); // "upcast" Derived d2 = getBase(); // CASE 2 ... } 

I am using VS2008 with optimizations enabled (/ Ox / Ob2 / Oi / Ot). I checked the calls to the designers and destructors on the console output:

Case 1 optimizes return value. There are two challenges:

  • Base (integer)
  • ~ Base ()

However, there is nothing to win when the main object needs a Derived-object. Upcast requires another pair of constructors / destructors.

In Case 2, return value optimization does not work. Two objects are created and destroyed here:

  • Base (int) // Create Temporary
  • ~ Base () // Destroy temporary
  • Base (const Base &) // via Derived (const Base &)
  • ~ Base () // via ~ Derived ()

Now it seems to me that I have three conflicting requirements:

  • I would like to avoid the overhead of creating a temporary object (since creating and destroying objects is quite expensive in the Base class)
  • Basically I need a Derived object instead of the base object to work with.

Obviously there is no free lunch here. But I could have missed something. So my question is: is there a way to combine these requirements? Or did someone have similar experiences?

Sidenote: I am aware that the "upcast" Derived (const Base & other) can fail at runtime (this was taken care of). Since the code is okay at the syntax level, I would suggest that this is not a reason for the compiler to avoid RVO.

+4
source share
2 answers

This is bad.

 Derived(const Base& other) :Base(other) {...} 

The static type other may belong to a derived type. In this case, it will be chopped. In addition, this base class will be copied.

RVO about bypassing the copy constructor and initializing the return object in place. If you need an object of a derived type, you will have to build it first. RVO cannot create it for you.

Instead of Derived(const Base& other) you can consider a different approach. How about this:

 class Base { ... // extract expensive parts of another instance virtual void initialise(Base& b); ... }; class Derived : public Base { ... Derived(); // cheap constructor void initialise(Base& b) { /* implementation goes here */ } ... }; 

initialise(Base& b) method will extract the expensive parts from the argument. It can be devastating. The base will provide an open (or possibly secure) interface for actual retrieval.

+2
source

How to add a constructor to Derived ?

 Derived(Base (*f)(void)) : Base(f()) { ... } 

Then Derived d3 = getBase; can help you in optimization. This is probably not very practical, since I had to specify an empty list of f parameters in Derived , which is quite limiting. But make it a template constructor, and you can use a functor written by the user, the result of boost:bind or the C ++ 0x lambda available there.

Otherwise, extract the id = ... part of getBase into the getId function and give Derived constructor that takes an int that passes the id to its Base sub-object. Without a doubt, the actual code is more complex than that, and this can lead to many parameters being loaded. Perhaps this is the lightweight BaseParameters class that you use instead of Base until you need slow work, and then convert it to Base , Derived or another sibling class.

0
source

All Articles