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 Base can be "upcast" by means of the second constructor of Derived. 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 turned on (/Ox /Ob2 /Oi /Ot). I checked calls to constructors and destructors on the console-output:
In Case 1 return value optimization works. There are two calls to:
However, there is nothing to be won here, when a Derived-object is needed in main. The "upcast" requires another constructor/destructor pair.
In Case 2 return value optimization does not work. Two objects are created and destroyed here:
Now it seems to me, that I have three conflicting requirements:
Obviously, there's no free lunch here. But I might have missed something. So my question is: Is there a way to combine these requirements? Or has anyone had similar experiences?
Sidenote: I am aware of the fact, that the "upcast" Derived(const Base& other) might fail during runtime (this has been taken care of). Since the code is ok on syntactic level, I'd guess this not the reason for the compiler to avoid RVO.
This is bad.
Derived(const Base& other) :Base(other) {...}
The static type of other
can belong to the derived type. In that case it will get sliced. On top of that base class would be copied anyway.
RVO is about bypassing copy constructor and initialising the returned object in-place. If you need an object of the derived type you would have to to construct it first. RVO can't construct it for you.
Instead of Derived(const Base& other)
you may want to consider 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 would extract expensive parts from the argument. It can be destructive. Base will provide public (or maybe protected) interface to do the actual extraction.