2011-04-15 29 views

回答

9
std::string get(){ 
    // this is similar to return std::string("..."), which is 
    // copied/moved into the return value object. 
    return "..."; 
} 

RVO允许它直接构造临时字符串对象到的get()返回值对象。

foo(get()); 

RVO允许它直接构建临时字符串对象(返回值对象)直接进入foo参数对象。

这些是允许的RVO方案。如果您的编译器无法应用它们,则将分别使用移动构造函数(如果可用)将返回值移动到返回值对象和参数对象中。在这种情况下,这并不奇怪,因为无论如何这两个临时对象都被视为右值。 (对于第一种情况,没有表达式对应于创建的临时表达式,因此处理仅用于选择将临时数据复制/移动到返回值对象时使用的构造函数)。

对于其它情况,编译器必须考虑的事情作为右值,即使他们在其他方面的左值

std::string get(){ 
    std::string s = "..."; 
    // this is similar to return move(s) 
    return s; 
} 

细则中指出,当它可能通过它设置的规则适用RVO(或NRVO)到左值需要实现将表达式视为rvalues并使用移动构造函数(如果可用),并且只有在找不到合适的构造函数时,才应该使用该表达式作为左值。如果程序员明确表示程序员总是需要移动而不是副本,那么程序员在这些情况下写出明确的动作可能会很遗憾。

实施例:

struct A { A(); A(A&); }; 
struct B { B(); B(B&&); }; 

A f() { A a; return a; } 
B f() { B b; return b; } 

对于第一,它需要a作为右值,但无法找到接受此右值(A&不能结合右值)的构造。因此,它再次将a视为它(左值)。第二,它需要b作为右值,并且有B(B&&)取该值并移动它。如果它将b作为左值(它是什么),那么复制初始化将失败,因为B没有隐式声明的复制构造函数。


注意,返回并放慢参数通过使用拷贝初始化,这意味着

u -> T (where u's type is different from T) => 
    T rvalue_tmp = u; 
    T target(rvalue_tmp); 

t -> T (where t's type is T) => 
    T target = t; 

因此,在这个例子中,我们返回"..."的规则,我们首先创建一个右值暂时的,然后移动进入目标。对于返回返回值/参数类型表达式的情况,我们将直接将表达式复制到目标中。

1

实现定义,但最有可能复制省略。

类似地,当从函数返回对象值时,RVO/NRVO很可能会在移动语义之前启动。

2

最有可能的复制椭圆,但如果你的编译器不能应用在这种情况下,如果函数更复杂可能会发生,那么你正在寻找一个动作。移动效率非常高,所以如果没有执行ellision,我不会在这里发生恐慌。