我对RVO的期望有疑问(我知道在C ++ 17之前没有任何保证)。对于以下示例:
std::vector<Foo> getFooVectorFromSomewhere();
class A
{
private:
std::vector<Foo> foo_;
public:
void doSomething()
{
foo_ = getFooVectorFromSomewhere();
}
}
我试图减少在代码的特别热点中发生的内存分配/释放量。以上是否允许getFooVectorFromSomewhere()
重用foo_
的capactity,或者这会导致每次内存分配?我想远离通过引用传递foo_
并在函数内清除它,如果可能的话,为了代码清晰起见。 (在这种情况下,foo_严格是输出,我只想清除它并在可能的情况下重用它。)
感谢。
答案 0 :(得分:4)
Elision(RVO)仅适用于函数调用的返回,如果您直接使用它来初始化对象。由于foo_
已经存在,因此省略不适用。
在执行赋值语句时,您有两个vector
s:foo_
,以及从getFooVectorFromSomewhere
返回的prvalue。这些vector
中的每一个都可能有自己的分配。
处理此问题的最有效方法是销毁foo_
中的任何分配,并将分配从prvalue移动到该对象中。并且......这正是将要发生的事情(在C ++ 11中)。这称为“移动分配”。它涉及摧毁foo_
中的所有内容,然后是几个指针副本并使指针无效。
如果这是预先C ++ 11,那么它将不得不从一个vector
复制到另一个foo_
。如果它这样做,那么如果它足够大,它可以重用<div class="stackOvExample">Your content</div>
中的分配。如果不是,那么它将不得不做一个更大的。
在C ++ 11中是否有任何方法可以让编译器在进行新的分配之前意识到foo_的分配将被破坏并尝试在函数内部重用该容量(只要它具有足够的容量)并在函数结束时移动结果?
没有。将函数的返回值赋给变量不会导致编译器以某种方式使该变量成为函数接口的一部分,以便函数可以访问和操作它。这就是为什么elision仅适用于初始化对象,而不是对现有对象的任意赋值。该函数有效地作用于未初始化的内存,但它只能通过初始化它作用于该内存。命名RVO允许函数进行后初始化工作,但初始化它是它的工作的一部分。
如果您希望函数以这种方式操作活动对象,则它必须是函数接口的显式部分,作为参数传递。
答案 1 :(得分:0)
我一遍又一遍地看到这种误解。 C ++ 17 不保证(N)RVO!我不知道这种误解源于何处,但很多人都有同感。在某些情况下,C ++ 17保证复制省略,但(N)RVO 不其中之一。
在您的情况下,根本就没有NRVO!在这里,std::vector
移动分配将启动。(N)RVO可以在您getFooVectorFromSomewhere
的上下文中讨论,但由于未提供代码,因此无法进行讨论。
答案 2 :(得分:0)
代码foo_ = getFooVectorFromSomewhere();
将使用与std::vector
关联的移动分配运算符。如果你的实现正确地优化了这个操作符,那么它只需交换与旧缓冲区和新缓冲区相关的指针,并完全避免内存分配。
目前尚不清楚这段代码的替代意义,我们无法保证/评估它是否会在不看到它取代的情况下提高性能。