考虑以下两个类:
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() {}
...
};
这意味着 Base 可以通过 Derived 的第二个构造函数“upcast”。 现在考虑以下代码:
Base getBase()
{
int id = ...
return Base(id);
}
...
int main()
{
Base b = getBase(); // CASE 1
Derived d1(b); // "upcast"
Derived d2 = getBase(); // CASE 2
...
}
我正在使用VS2008打开优化(/ Ox / Ob2 / Oi / Ot)。我在console-output上检查了对构造函数和析构函数的调用:
在案例1 中,返回值优化有效。有两个电话:
但是,当 main 中需要派生对象时,这里没有什么可赢的。 “upcast”需要另一个构造函数/析构函数对。
在案例2 中,返回值优化不起作用。这里创建并销毁了两个对象:
现在在我看来,我有三个相互矛盾的要求:
显然,这里没有免费的午餐。但我可能错过了一些东西。所以我的问题是:有没有办法结合这些要求?或者有没有人有类似的经历?
旁注:我知道“upcast”Derived(const Base& other)在运行时可能会失败(这已经得到了解决)。由于代码在语法层面上没问题,我猜这不是编译器避免RVO的原因。
答案 0 :(得分:2)
这很糟糕。
Derived(const Base& other) :Base(other) {...}
other
的静态类型可以属于派生类型。在那种情况下,它将被切片。最重要的是,该基类将被复制。
RVO是绕过复制构造函数并就地初始化返回的对象。如果需要派生类型的对象,则必须先构造它。 RVO无法为您构建它。
而不是Derived(const Base& other)
您可能需要考虑不同的方法。怎么样:
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)
方法会从参数中提取昂贵的部分。它可能具有破坏性。 Base将提供公共(或可能受保护)的接口来进行实际提取。
答案 1 :(得分:0)
如何将构造函数添加到Derived
?
Derived(Base (*f)(void)) : Base(f()) { ... }
然后Derived d3 = getBase;
可能会为您提供所需的优化。可能不太实际,因为我必须在f
中指定Derived
的空参数列表,这是非常有限的。但是使它成为一个模板构造函数,你可以使用一个用户编写的函子,一个boost:bind
或一个C ++ 0x lambda的结果。
如果失败,请将id = ...
getBase
部分提取到函数getId
中,并为Derived
提供一个int
的构造函数,该id
传递Base
转到其BaseParameters
子对象。毫无疑问,真正的代码比这更复杂,并且可能导致拖拽有关该地方的许多参数。也许是一个轻量级Base
类,您可以使用它代替Base
,直到您实际需要完成缓慢的工作,然后将 转换为Derived
,{{1}}或其他相关课程。