我正在阅读有关move semantics的内容,并提供了以下代码:
#include <iostream>
using namespace std;
vector<int> doubleValues (const vector<int>& v)
{
vector<int> new_values;
new_values.reserve(v.size());
for (auto itr = v.begin(), end_itr = v.end(); itr != end_itr; ++itr )
{
new_values.push_back( 2 * *itr );
}
return new_values;
}
int main()
{
vector<int> v;
for ( int i = 0; i < 100; i++ )
{
v.push_back( i );
}
v = doubleValues( v );
}
所以作者说在doubleValues
返回后,最多可能发生两个副本
一个成为临时对象返回,第二个时候返回 向量赋值运算符在第v = doubleValues(v)行上运行;
他还声明可以优化第一份副本。
这是我没有得到的:
在说“一个人要归还临时物品”时他的意思是什么?它不是函数返回的临时对象吗?如果是这样,我不明白为什么应该将任何东西复制到另一个临时对象。
他说这个临时对象可以被优化掉。如何优化临时对象,例如什么被认为是优化?
答案 0 :(得分:2)
重要的是要理解编译器必须做什么以及编译器可以做什么与正式>强>意味着它意味着什么。
从您发布的代码段i++
获取一个更小更简单的示例。
您的代码正式的含义是:复制i
,然后增加i
,并使表达式具有您之前创建的副本的值。
您的代码真正的含义是:增加i
并将其余内容搞定(因为我没有使用结果)。
编译器必须做的是完全实现&#34;(代码正式意味着&#34; 部分)的(外部可见)语义。没有更多,也没有
编译器可以做的是递增i
并将其余部分搞定(因为没有人可以区分)。
通常,编译器可以基于&#34;进行任何它喜欢的更改,就好像&#34;规则,这意味着,只要外部可观察的行为保持不变,没有人关心。移动语义是一种隐式和显式地进一步弯曲规则的方法。
当您移动对象而不是复制它时,您基本上是在作弊。然而,&#34;作弊&#34;和#34;魔术&#34;是你是否被抓住(就像&#34;天才&#34;&#34;疯狂&#34;之间的区别仅仅取决于成功)。
你无论如何都会滥用一个将在下一个瞬间被摧毁的物体,声称它不是一个副本。但是,没有人会注意到,因为移动的对象是一个右值,即它没有名称(因此无法访问)并且也会立即被销毁。另一方面,你呈现给世界的新物体根本不是新的,但它无论如何都是一个非常好的物体(原始物体!)。这就是整个技巧。
关于您的第二个问题:您的doubleValues
函数通过常量引用获取std::vector
。那不是副本,它和指针一样便宜。但是,然后将所有值复制到新对象new_values
中
然后,您按值返回该对象,这意味着您正式制作第二个副本,最后您调用std::vector
的赋值运算符,实际上,它甚至是第三个复制。
正式,就是这样。优化编译器可能并且希望将其优化(NRVO) 曾经是编译器只能用未命名的临时工做,但那就像... 10年前。如今,通常/经常命名的返回值优化&#34;只是运作良好。
在某些情况下(虽然不在您的示例中),编译器甚至必需按照最新标准执行copy elision。
答案 1 :(得分:1)
您需要了解,如果函数返回值按值,则会通过临时返回。这意味着,非优化编译器将:
new_values
复制到临时的return
v
,则main()
醇>
该标准允许编译器删除任何这些副本,如果可以的话。例如,它可以将隐藏的引用参数传递给v
中的doubleValues
,doubleValues
可以使用此隐藏引用而不是new_values
。这样,就不会执行任何复制。
注意:c ++ 11编译器编译器将使用move而不是copy