c++ - 为什么 std::move 会阻止 RVO(返回值优化)?

标签 c++ c++11 move-semantics return-value-optimization

在许多情况下,当从函数返回一个局部变量时,RVO(返回值优化)会起作用。但是,我认为显式使用 std::move 至少会在 RVO 不执行 move 时强制执行发生,但在可能的情况下仍会应用 RVO。然而,似乎并非如此。

#include "iostream"

class HeavyWeight
{
public:
    HeavyWeight()
    {
        std::cout << "ctor" << std::endl;
    }

    HeavyWeight(const HeavyWeight& other)
    {
        std::cout << "copy" << std::endl;
    }

    HeavyWeight(HeavyWeight&& other)
    {
        std::cout << "move" << std::endl;
    }
};

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return heavy;
}

int main()
{
    auto heavy = MakeHeavy();
    return 0;
}

我用 VC++11 和 GCC 4.71 测试了这段代码,调试和发布 (-O2) 配置。复制 ctor 永远不会被调用。 move ctor 仅在调试配置中由 VC++11 调用。实际上,特别是这些编译器似乎一切都很好,但据我所知,RVO 是可选的。

但是,如果我明确使用 move:

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return std::move(heavy);
}

总是调用 move ctor。所以试图让它“安全”会让事情变得更糟。

我的问题是:

  • 为什么 std::move 会阻止 RVO?
  • 什么时候最好“抱最好的希望”并依赖 RVO,什么时候应该明确使用 std::move?或者,换句话说,如果不应用 RVO,我怎样才能让编译器优化完成它的工作并仍然强制 move ?

最佳答案

允许复制和 move 省略的情况参见标准(版本 N3690)的第 12.8 节第 31 节:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
  • [...]
  • 当一个未绑定(bind)到引用(12.2)的临时类对象将被复制/move 到具有相同cv-unqualified类型的类对象时,可以通过直接构造临时对象来省略复制/move 操作进入省略的复制/move 的目标
  • [...]

(我省略的两种情况是指抛出和捕获异常对象的情况,我认为这对优化不太重要。)

因此,如果表达式是局部变量的名称,则只有在返回语句中才会出现复制省略。如果您编写 std::move(var),那么它不再是变量的名称了。因此,如果它应该符合标准,编译器就不能忽略这个 Action 。

Stephan T. Lavavej 在 Going Native 2013 上谈到了这个问题。 (Alternative source)并准确解释了您的情况以及为什么要避免 std::move() 在这里。从 38:04 开始观看。基本上,当返回返回类型的局部变量时,它通常被视为右值,因此默认启用 move 。

关于c++ - 为什么 std::move 会阻止 RVO(返回值优化)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19267408/

相关文章:

c++ - Linux 上没有控制台应用程序

c++ - C++ 的 std::regex 中有最大组限制吗?

c++ - move 语义何时适用于 std::move?

c++ - 如果函数调用是 return 语句,编译器能否自动 move 函数参数?

c++ - 如何反转数组中的元素?

c++ - 在堆栈与堆中没有构造函数的结构/类的初始化

C++ wxWidgets Code::Blocks 自定义项目 "normal"编译和 C++11 启用期间的交叉编译链接错误

c++ - move 构造函数问题

c++ - LLVM ParseIR 段错误

c++ - 我怎样才能将 std::map 的迭代器递减一定数量?