c++ - 返回值优化和副作用

标签 c++ c++11 compiler-optimization

返回值优化 (RVO) 是一种涉及复制省略的优化技术,它消除了在某些情况下为保存函数返回值而创建的临时对象。我总体上了解 RVO 的好处,但我有几个问题。

该标准在 this working draft 的第 12.8 节第 32 段中对此进行了如下说明(强调我的)。

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or 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.

然后它列出了当实现可以执行此优化时的一些标准。


我有几个关于这种潜在优化的问题:

  1. 我习惯于优化受到约束,以至于它们无法改变可观察到的行为。此限制似乎不适用于 RVO。 我是否需要担心标准中提到的副作用?是否存在可能导致麻烦的极端情况?

  2. 作为程序员,我需要做什么(或不做什么)才能执行此优化?例如,以下是否禁止使用复制省略(由于移动):

std::vector<double> foo(int bar){
    std::vector<double> quux(bar,0);
    return std::move(quux);
}

编辑

我将此作为一个新问题发布,因为我提到的具体问题在其他相关问题中没有直接回答。

最佳答案

I am used to optimizations being constrained such that they cannot change observable behaviour.

这是正确的。作为一般规则 - 称为 as-if 规则 - 如果更改不可观察,编译器可以更改代码。

This restriction does not seem to apply to RVO.

是的。 OP 中引用的子句为 as-if 规则提供了一个异常(exception),并允许省略复制构造,即使它有副作用。请注意,RVO 只是复制省略的一种情况(C++11 12.8/31 中的第一个要点)。

Do I ever need to worry about the side effects mentioned in the standard?

如果复制构造函数具有副作用,例如执行复制省略会导致问题,那么您应该重新考虑设计。如果这不是您的代码,您可能应该考虑一个更好的替代方案。

What do I as a programmer need to do (or not do) to allow this optimization to be performed?

基本上,如果可能,返回一个与函数返回类型具有相同 cv 非限定类型的局部变量(或临时变量)。这允许 RVO 但不强制执行(编译器可能不执行 RVO)。

For example, does the following prohibit the use of copy elision (due to the move):

// notice that I fixed the OP's example by adding <double>
std::vector<double> foo(int bar){
    std::vector<double> quux(bar, 0);
    return std::move(quux);
}

是的,因为您没有返回局部变量的名称。这个

std::vector<double> foo(int bar){
    std::vector<double> quux(bar,0);
    return quux;
}

允许 RVO。有人可能会担心,如果不执行 RVO,那么移动比应对要好(这可以解释上面 std::move 的使用)。别担心。所有主要编译器都将在此处执行 RVO(至少在发布版本中)。即使编译器不执行 RVO 但满足 RVO 的条件,它也会尝试执行移动而不是复制。总之,使用 std::move上面肯定会出手。不使用它可能既不会复制也不会移动任何东西,并且在最坏(不太可能)的情况下会移动。

(更新: 正如 haohaolee 所指出的(见评论),以下段落是不正确的。但是,我将它们留在这里,因为它们提出了一个可能适用于没有的类的想法构造函数采用 std::initializer_list(参见底部的引用资料)。对于 std::vector,haohaolee 找到了解决方法。)

在这个例子中,你可以通过返回一个 braced-init-list 来强制使用 RVO(严格来说这不再是 RVO,但为了简单起见我们继续这样调用),返回类型可以是创建:

std::vector<double> foo(int bar){
    return {bar, 0}; // <-- This doesn't work. Next line shows a workaround:
    // return {bar, 0.0, std::vector<double>::allocator_type{}};
}

查看 postR. Martinho Fernandes的辉煌answer .

小心!返回类型是否为 std::vector<int>上面的最后一个代码将具有与原始代码不同的行为。 (这是另一个故事。)

关于c++ - 返回值优化和副作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19792135/

相关文章:

c++ - 为什么 memory_order_relaxed 在 x86 上使用原子(锁前缀)指令?

c++ - C++ Eigen避免或检测广播功能中的零除

c++ - 在基于范围的循环中使用通用引用有什么好处?

c++ - regex_search 抛出 std::regex_error

linux - recvfrom() 在接收到 udp 数据包时解除阻塞,但返回 0 作为读取字节的大小(使用 oscpack)

c++ - gcc 使用专门的定义内联通用模板函数

haskell - 为什么 Haskell 中没有隐式并行性?

c++ - 为什么交换没有完成?

c++ - 第一个模板中的 "::type=0"是什么意思?

c++ - 如何在内部函数中使用 if 条件