这是绑定到非RVO返回值的惯用方法吗?

时间:2018-06-17 20:46:12

标签: c++ rvalue-reference return-value-optimization

让我们假设一个不能改变的示例性功能(例如,因为它没有手头的源代码而被链接),并且它不能受NRVO的约束。如下所示:

auto f()
{
    std::vector<int> v1 { 3, 2, 1 };
    std::vector<int> v2 { 2, 1, 3 };

    /* RVO impossible. */
    return someRuntimeCondition ? v1 : v2;
}

在来电者方面,

auto&& vec = f(); /* Extend lifetime to current scope. */

std::sort(vec.begin(), vec.end()); /* Const-lvalue ref. was no option. */

这应该避免从返回值移动构造本地std::vector。但我不经常看到这样的片段。我对上述片段的推理是错误的还是像f()这样的函数如此罕见以至于依赖于RVO是更惯用的评论?

我要求了解如何在假设RVO不适用的情况下绑定f()的返回值。

2 个答案:

答案 0 :(得分:3)

从C ++ 17开始,如果函数按值返回,则以下两个选项完全相同:

  • auto a = f();
  • auto&& a = f();

除了decltype(a)的结果。在两种情况下,结果对象都由a命名,结果对象由函数中的return语句直接初始化。

在C ++ 17之前,结果对象始终是临时的,auto a = f();复制初始化a来自临时对象,此操作可选择为elidable。

有些人确实使用了做auto&& a = f();的做法来防范没有实现可选副本省略的编译器。这有利有弊。

我个人使用前者是因为我使用C ++ 17,甚至在此之前我只使用了最大复制省略的编译器。除了在调试模式下运行的旧版MSVC之外,我实际上并不知道任何没有复制省略的编译器。

但我可以想象,为使用旧编译器的客户编写代码库的人可能会因为性能原因而感觉需要修改代码。

答案 1 :(得分:2)

我不知道处理这种问题的惯用方法,但你总能做到这样的事情:

auto f()
{
    std::vector<int> v1 { 3, 2, 1 };
    std::vector<int> rv { 2, 1, 3 }; // return value

    if(someRuntimeCondition)
        std::swap(v1, rv);

    return rv;
}

所以你总是确保你只返回一个物体;

对我来说,另一种方法是这样的:

auto f()
{
    std::vector<int> v1 { 3, 2, 1 };
    std::vector<int> v2 { 2, 1, 3 };

    return std::vector<int>{std::move(someRuntimeCondition ? v1 : v2)};
}

这应该调用C++17 保证 RVO,在运行时将任意一个向量移动到调用者的堆栈中。