7
vector<int> f(const vector<int>& v)
{
    vector<int> ret(v.size());
    fill(ret.begin(), ret.end(), 123);
    copy(v.begin(), v.end(), ret.begin());
    return ret;
}

int main()
{
    vector<int> v(10);
    v = f(v);
}

如果对 f 应用返回值优化,则局部变量 ret 与 main 中的 v 共享相同的地址。但如果这是真的,填充 ret 将在复制之前丢弃 v 中的数据。没有 RVO 的代码是正确的,优化不应该破坏行为。

这是安全的还是我不正确理解 RVO?

4

3 回答 3

5

What happens is this:

On the caller's side, a return slot is provided which can hold the result, that means that the caller provides the memory for the variable of type std::vector<int>. It expects the called method to construct the value and is itself responsible for calling the destructor when the result is no longer used and freeing the memory (if necessary, it probably just lives on the stack).

The called function (which may live in a different translation unit!) would, without the NRVO, so this:

  • Provide a memory slot for ret.
  • Construct a local variable ret in this memory slot.
  • Do stuff...
  • Copy-construct the return value in the provided memory slot by copying ret.
  • Call ret's destructor.

Now, with the NRVO, the decision to optimize this can be done in the called function's translation unit. It transforms the above into:

  • Construct ret in the memory of the method's return slot.
  • Do stuff...

No need to do anything else as the memory is owned and the destructor is called by the caller and because the optimization is transparent for the caller :)

This, of course, can't eliminate the assignment into v in your example. If you store the result in a different variable, e.g.

std::vector<int> w = f(v);

the NRVO will construct ret directly into w's memory (as this will be passed in as the return slot to f).

于 2013-09-06T16:54:19.753 回答
5

代码是正确的,你对 RVO 的理解不是——特别是这个:

the local variable ret shares the same address as v

它不共享您分配给它的变量的地址。从技术上讲,当您返回一个自动局部变量时,它将被复制到一个临时变量中。RVO 跳过了那部分。

它是这样的:

         create variable `ret`
                   |
 copy ret to a temporary before returning
                   |
       assign the temporary to v

NRVO(在这种情况下)将跳过第二部分。

编译器虽然很聪明,所以如果它只是优化整个事情不要感到惊讶,因为根本没有可观察到的行为。

于 2013-09-06T16:38:22.860 回答
0

RVO 涉及新对象的构建。显然,在 main 中,编译器不能v作为返回值的地址传递,因为函数会在这个地址处构造一个 new ,并且已经构造好了。所以它会传递一个临时的地址。RVO 关注函数中发生的事情:没有 RVO,编译器将构造vector<int>vret,然后将其复制到作为隐藏参数传递的地址中。在调用点,如果返回值被用于(复制)构造一个对象,那么编译器将跳过临时复制到新对象中,而只是传递要构造的对象的地址。但是,如果返回值没有被用作复制构造函数的参数,那么编译器就无能为力了。您需要一个正确类型的对象,因此它必须生成一个临时对象。

于 2013-09-06T17:16:39.000 回答