当分配给不同类型时,返回值优化是否有效?
考虑以下两个类:
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的第二个构造函数进行“向上转换”。 现在考虑以下代码:
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)。我在控制台输出上检查了对构造函数和析构函数的调用:
在情况 1 中,返回值优化有效。有两次调用:
- Base(int)
- ~Base()
但是,当 main 中需要 Derived 对象时,这里没有任何好处。 “向上转换”需要另一个构造函数/析构函数对。
在情况2中,返回值优化不起作用。这里创建和销毁两个对象:
- Base(int) //创建临时
- ~Base() //销毁临时
- Base(const Base&) //via Derived(const Base&)
- ~Base() //via ~Derived()
现在在我看来,我有三个相互冲突的要求:
- 我想避免以下开销创建临时对象(因为对象在Base类中创建和销毁相当昂贵)
- 在main中,我需要一个派生对象而不是Base >-要使用的对象。
显然,天下没有免费的午餐。但我可能错过了一些东西。所以我的问题是:有没有办法结合这些要求?或者有人有过类似的经历吗?
旁注:我知道“upcast” Derived(const Base& other) 在运行时可能会失败(这已得到解决)。由于代码在语法层面上没有问题,我猜这不是编译器避免 RVO 的原因。
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:
- Base(int)
- ~Base()
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:
- 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'd like to avoid the overhead of creating the temporary object (because object creation and destruction is rather expensive in class Base)
- In main, I need a Derived-object instead of a Base-object to work with.
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这很糟糕。
other
的静态类型可以属于派生类型。在这种情况下,它将被切片。无论如何,都会在该基类之上进行复制。RVO 是绕过复制构造函数并就地初始化返回的对象。如果您需要派生类型的对象,则必须首先构造它。 RVO 无法为您构建它。
您可能需要考虑不同的方法,而不是
Derived(const Base& other)
。怎么样:initialise(Base& b)
方法将从参数中提取昂贵部分。它可能具有破坏性。 Base 将提供公共(或可能受保护)接口来进行实际提取。This is bad.
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: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.向
Derived
添加一个构造函数怎么样?然后
Derived d3 = getBase;
可能会得到您想要的优化。可能不太实用,因为我必须在Derived
中指定f
的空参数列表,这是相当有限的。但将其设为模板构造函数,您就可以使用用户编写的函子、boost:bind 的结果或 C++0x lambda(如果可用)。如果失败,请将
getBase
的id = ...
部分提取到函数getId
中,并为Derived
提供一个构造函数采用int
将id
传递给其Base
子对象。但毫无疑问,真实的代码比这更复杂,这可能会导致拖拽大量有关该位置的参数。也许是一个轻量级BaseParameters
类,您可以使用它来代替Base
,直到您真正需要完成缓慢的工作,然后将其转换到Base
、Derived
或其他相关类。How about adding a constructor to
Derived
?Then
Derived d3 = getBase;
might get you the optimization you want. Probably not very practical as it is, since I've had to specify the empty parameter list off
inDerived
, which is quite limiting. But make it a template constructor and you can use a user-written functor, the result of aboost:bind
or a C++0x lambda where available.Failing that, extract the
id = ...
part ofgetBase
into a functiongetId
, and giveDerived
a constructor takingint
that passes theid
on to itsBase
sub-object. No doubt the real code is more complex than this, though, and that might result in hauling a lot of parameters about the place. Maybe a lightweightBaseParameters
class, which you use in place ofBase
until the point where you actually need the slow work done, and then convert that toBase
,Derived
, or another related class.