【发布时间】:2015-03-09 23:53:05
【问题描述】:
我有一个“构建”要返回的结构的函数:
struct stuff {
int a;
double b;
Foo c;
};
stuff generate_stuff() {
Foo c = generate_foo();
//do stuff to Foo, that changes Foo:
//...
return {1, 2.0, c}; //should this be return {1, 2.0, move(c)};?
}
我应该将c 移出该功能吗?我意识到,(N)RVO 经常可以就地构建对象,但有时可能并非如此。什么时候不能完成 (N)RVO,因此,我应该什么时候移动函数的对象?
换句话说,这显然是返回的临时的 RVO。问题变成了,NRVO(命名返回值优化)会发生在c 上吗?将c就地构造(在函数的调用点,在临时的stuff结构内),还是将c在函数中构造,然后复制到调用点的结构中。
【问题讨论】:
-
这构造了一个
stuff类型的临时文件:它将保存c的副本(作为左值,c不能移动到这个临时文件中)。然后这个临时本身可以被移出函数,并且它的Foo成员(本身是c的副本)将在那时被移动。 -
如果
Foo包含内置类型,那么它不会产生影响,但假设移动构造Foo比复制构造它更便宜,你应该使用std::move(c)。复制省略允许在该实例中省略stuff的复制/移动,但同样不适用于构成stuff的数据成员的构造。 -
RVO 仅适用于临时对象,如果要返回的表达式是名称,则 NRVO 仅适用于返回语句。因此,
return {1, 2.0, generate_foo()};和stuff s = {1, 2.0, generate_foo()}; /* do something */ return s;都可以使用 (N)RVO。 -
@Praetorian:可以假设 Foo 不是 POD。 @IgorTandetnik:因此,
stuff类型的临时对象将在调用站点 (RVO) 构建,问题是c是否将构建到位,还是必须移动。 -
不,这不是优化。标准要求
return {..};直接初始化返回值。这与return表达式;不同,注意花括号初始化列表不是表达式。