C++是否有类似RVO的东西用于将临时对象传递给函数

C++ Is there something like RVO for passing temporary objects to functions?

本文关键字:临时对象 用于 函数 是否 RVO C++      更新时间:2023-10-16

使用RVO,可以在不移动或复制的情况下从返回的对象创建对象。在将对象传递给函数时,是否可以执行类似的操作?

class SomeClass {
int a;
public:
SomeClass(int _a)                   {a = _a;        std::cout << "createdn";}
SomeClass(const SomeClass& source)  {a = source.a;  std::cout << "copiedn";}
SomeClass(SomeClass&& source)       {a = source.a;  std::cout << "movedn";}
};
void create(SomeClass&& source) {
SomeClass some_object( std::move( source));
//p_some_object = new SomeClass( std::move( source));
//new( p_some_object) SomeClass( std::move( source));
}
// ...
create(SomeClass(15));

在本例中,结果为"创建、移动"。(GCC 5.4/C++11/-O3)与新的或放置新的相同。没有分支或任何东西,为什么不能在不移动的情况下创建它?

不能保证编译器必须进行足够的内联和分析,以识别出source引用的临时对象是不必要的。除此之外,即使是RVO也适用于价值语义;当&&引用开始发挥作用时,不能保证。

碰巧的是,如果你遵循C++函数参数的一般规则,GCC会很好地处理你的情况:"如果你无论如何都需要复制参数,按值接受参数。">

通过值接受意味着,在这种情况下,参数被构造为调用的一部分,不需要复制或移动(它可以,在GCC上,是"RVO"-ed)。

class SomeClass {
int a;
public:
SomeClass(int _a)                   {a = _a;        std::cout << "createdn";}
SomeClass(const SomeClass& source)  {a = source.a;  std::cout << "copiedn";}
SomeClass(SomeClass&& source)       {a = source.a;  std::cout << "movedn";}
};
void create(SomeClass some_object) {
}
int main() {
create(SomeClass(15));
}

如果您尝试它,它只显示created消息。

如果目标是让create为您构建对象,那么使用带有参数转发的模板函数,以便在create中构建它,例如

template<typename T, class... Args>
T create(Args&&... args) {
T some_object(std::forward<Args>(args)...);
return some_object;
}
int main() {
auto foo = create<SomeClass>(15);
}

它通过将构造函数参数作为转发引用传递,并在create中构造对象本身来解决问题,避免了任何额外的构造,如图所示。

RVO不会在引用中发生,也不会在从函数参数创建另一个对象时发生。

如果您将参考参数更改为值参数,则RVO将应用于它,而不是本地对象:

void create(SomeClass source) { 
SomeClass otherObject = std::move(source); // No RVO, object constructed from parameter
}
// ...    
create(SomeClass{15}); // RVO applied to source parameter

然而,您可以使用lambda作为函数参数来实现它:

template<typename C>
void create(C source) {
SomeClass some_object{source()};
}
// ...
create([]{ return SomeClass{15}; });

您可以检查实时输出