2017-07-27 81 views
-3

由于来自社区的广泛响应,我希望能够从堆栈溢出用户中推断特定于实现的响应。std :: move()在对象构造中返回语句帮助还是防止RVO?

哪些是最佳做法(提供最大优化)?

// version 1 
MyObject Widget::GetSomething() { 
    return MyObject(); 
} 

// version 2 
MyObject Widget::GetSomething() { 
    return std::move(MyObject()); 
} 

// version 3 
MyObject Widget::GetSomething() { 
    auto obj = MyObject() 
    return obj; 
} 

// version 4 
MyObject Widget::GetSomething() { 
    auto obj = MyObject() 
    return std::move(obj); 
} 

编辑: 谢谢Yakk,对于直接的,恭敬的回答。 [接受的答案]

+3

“来自社区的广泛回应” - 例子?这在AFAIK的共识中是不好的。 –

+0

你错过了我喜欢的那个:'return {};' –

+0

“提供了最大的优化”本质上是一个特定于实现的问题 –

回答

2
// version 1 
MyObject Widget::GetSomething() { 
    return MyObject(); 
} 

在C++ 03中这需要MyObject通过copyable。在运行时,如果标准允许在此处使用elision,则不会使用具有合理设置的任何“真实”编译器进行复制。

在C++ 11或14中,它要求对象是可移动或可复制的。 Elision仍然存在;没有移动或复制完成。

在C++ 17中,没有任何移动或拷贝在这里可以退出。

在任何情况下,实际上,MyObject都是直接在返回值中构造的。

// version 2 
MyObject Widget::GetSomething() { 
    return std::move(MyObject()); 
} 

这在C++ 03中无效。

在C++ 11及更高版本中,将MyObject移入返回值中。此举必须在运行时发生(禁止如果消除)。

// version 3 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    return obj; 
} 

与版本1相同,除了C++ 17的行为类似于C++ 11/14。另外,这里的elision更脆弱;看似无害的更改可能会迫使编译器实际移动obj。在C++ 11/14/17中(以及C++ 03中的2个副本),理论上2个移动都被忽略了。第一个安全,第二个脆弱。

// version 4 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    return std::move(obj); 
} 

在实践中,这种行为就像版本2的额外移动(复制在C++ 03)出现在构建obj但它会被省略,所以什么也不会发生在运行时。

Elision允许消除复制/移动的副作用;对象的生命期被合并到一个对象中,并且移动/复制被消除。构造函数仍然存在,它从未被调用过。

答案

1和3都将编译为相同的运行时代码。 3稍微脆弱一些。

2和4都编译成相同的运行时代码。它不应该比1/3更快,但是如果编译器证明没有这样做可以消除这种情况,那么就和编译时一样 - 如果这样做,它可以编译成与1/3相同的运行时代码。这远远没有保证,而且非常脆弱。

所以1>=3>=2>=4是在实践中更快到更慢的顺序,其中“更脆弱”的代码,否则是相同的速度是<=

这样情况的一个例子,可以使3比1更慢,如果你有一个if语句:

// version 3 - modified 
MyObject Widget::GetSomething() { 
    auto obj = MyObject(); 
    if (err()) return MyObject("err"); 
    return obj; 
} 

突然许多编译器将被迫移动obj到返回值,而不是eliding obj和返回值一起。