可以返回一个括起来的封闭初始化器导致C ++中的副本吗?

时间:2014-02-06 13:16:14

标签: c++ c++11 initializer-list return-value-optimization

示例:

struct s { int a; };

s func() { return {42}; }

int main() {
    s new_obj = func(); // line 6
    (void) new_obj;
    return 0;
}

这很有效。现在,如果我们假设我们的编译器没有RVO会发生什么呢?

  1. func会返回s的结构,因此{42}必须转换为s,然后返回并最终复制到第6行的new_obj
  2. func会返回初始化列表,因此无法进行深层复制。
  3. 语言是什么意思?你能举一个证明吗?

    注意:我知道这在这个例子中似乎没什么用,但是对于返回非常大的,常量大小的std::array,我不想依赖RVO。

1 个答案:

答案 0 :(得分:5)

考虑以下示例:

#include <iostream>

struct foo {
    foo(int) {}
    foo(const foo&) { std::cout << "copy\n"; }
    foo(foo&&)      { std::cout << "move\n"; }
};

foo f() {
    //return 42;
    return { 42 };
}

int main() {
    foo obj = f();
    (void) obj;
}

使用带有-fno-elide-constructors的gcc 4.8.1进行编译时,防止RVO输出

move

如果在f中使用了没有花括号的return语句,则输出为

move
move

没有RVO,会发生以下情况。 f必须创建foo类型的临时对象,让我们将其称为ret,以便返回。

如果使用return { 42 };,则ret 直接从值42初始化。因此到目前为止还没有调用复制/移动构造函数。

如果使用的是return 42;,那么另一个临时的,我们称之为tmp是从42直接初始化,tmp被移动到创建ret。因此,到目前为止,一个移动构造函数被调用。 (注意tmp是一个右值,foo有一个移动构造函数。如果没有移动构造函数,那么将调用复制构造函数。)

现在ret是一个右值,用于初始化obj。因此,调用移动构造器以从ret移动到obj。 (同样,在某些情况下,可以调用复制构造函数。)因此,任何一个(return { 42 };)或两个(return 42;)移动都会发生。

正如我在对OP的问题的评论中所说,这篇文章非常相关: construction helper make_XYZ allowing RVO and type deduction even if XZY has noncopy constraint。特别是answer的优秀 R. Martinho Fernandes