首页 > 解决方案 > 如何确保执行 RVO 而不是复制?

问题描述

在许多情况下,我想创建一个新的数据实例并将其返回给 API 调用者。

我了解到unique_ptr/shared_ptr可用于工厂模式(例如,工厂模式 using unique_ptr in c++

同时,我了解到返回值优化 (RVO) 在许多编译器中都是可能的(例如,Efficient way to return a std::vector in c++)。

我更喜欢 RVO,因为它更容易使用没有包装的返回值unique_ptr并且更容易阅读代码,但是,由于 RVO 没有保证,我不想意外牺牲性能并且必须使用unique_ptr来确保返回值是moved而不是复制。

是否有任何方法可以明确指定要移动的返回值,以便如果 RVO 可能它不会抱怨任何事情,或者如果 RVO 不可能它会触发一些编译器警告?如果这是可能的,我可以安全地摆脱在这种情况下返回 unique_ptr 。

我正在使用 C++17,需要在 macOS 上支持 Apple Clang 11.0,在 Linux 上支持 g++ 9。

编辑:

我仍在学习 C++,并且在发布此问题时没有区分 RVO(返回值优化)和 NRVO(命名返回值优化)。在我看来,NRVO 在工厂方法等模式中更常见和有用,例如:

vector<foo> vec;
// populate data into vec
return vec;

我正在寻找类似的东西return std::move_only(returned_value),如果这个值不能移动(而不是复制移动),它会给我一个编译器警告。也许我应该将我的问题重新表述为:如果不能保证 NRVO,为什么在这个问题中“按值返回”仍然是推荐的方式(在 c++ 中返回 std::vector 的有效方式),答案不应该是“这取决于”您的功能实现以及您是否可以接受意外的性能成本?

标签: c++smart-pointersnrvo

解决方案


如何确保执行 RVO 而不是复制?

从 C++17 开始,该语言已经为您完成了这项工作。如果你有一个像

T foo() { /*stuff*/; return T{ /*stuff*/ }; }

然后,由于保证复制省略,可以保证省略返回的对象。

如果你有一个像

T foo() 
{
    T obj{ /*stuff*/ }; 
    // do stuff with obj
    return obj;
}

然后你会得到 NRVO(命名返回值优化),这是不保证的,或者编译器会移动obj,因为标准中有一条规则,所有具有自动存储持续时间的函数本地对象都将被移出函数,如果它们有移动构造函数。

这意味着您获得副本的唯一时间是如果您返回一个无法优化的对象(它是命名的本地对象或函数参数)并且它不支持移动。全局对象总是被复制,因为它们没有作用于函数。


推荐阅读