2017-07-06 55 views
3

我念叨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

@Destructor谢谢。不是说我没有浏览问答,但是这样回答**我的问题都是**还是只是最后一个?事情是,目前我没有看到第一个答案。 –

回答

1

你要明白,如果一个函数通过值返回一个值,然后通过临时返回。这意味着,非优化编译器将:

  1. 副本new_values到一个临时的,在return
  2. 副本从这个临时到v在最后行,如果main()

标准允许编译器如果可以的话,可以取消这些副本中的任何一个。例如,它可以将隐藏的参考参数传递给中的v,并且doubleValues可以使用此隐藏参考而不是new_values。这样,将不会执行复制。

注:一个C++ 11 compilant编译器将使用移动而不是拷贝

+0

啊,谢谢!那么这是否意味着一个空的临时对象被创建后,new_values的内容被复制到临时对象中,然后函数doubleValues的所有本地对象被销毁?如果是这样,我可以在哪里深入了解这个过程?再次感谢! –

+1

@FacPam:差不多。不是一个空的临时。临时将使用它的拷贝构造函数构造(在pre ++ C++ 11的情况下)。如果您现在了解了这一主要原则,那么您可以做的最好的方法是阅读标准的相关部分,该标准可以在线获得(只有草稿可用,但它们适用于此目的) – geza

+0

@FacPam:或者可能是某人可以推荐一本好书或者 – geza

3

,了解这一点很重要的是什么编译器必须做,什么编译器可以做是不同的东西是什么一块代码正式意味着什么,它的真正含义。

从您发布的代码片段中取一个更小更简单的示例:i++

你的代码正式方法是:使i副本,然后增加i,并有表达有你以前所做的副本的价值。
你的代码真的的意思是:增量i和拧其余(因为我没有使用结果)。

什么编译器必须做的就是实现完全“你的代码,正式意味着”部分的(外部可见)的语义。没有更多,也没有更少。
什么编译器可以做的是增量i和拧其余(因为没人能区分)。

一般来说,编译器可能会根据“as if”规则进行任何更改,而这只是表示只要外部可观察的行为保持不变,没有人关心。移动语义是一种手段,可以隐式和明确地将规则进一步弯曲。

当你移动一个对象而不是复制它时,你基本上是在作弊。然而,“作弊”和“魔法”之间的区别在于你是否被抓到(就像“天才”和“疯狂”之间的差异仅仅由成功定义一样)。
您正在滥用一个即将在下一个时间点被销毁的对象,声称这是在不是时的副本。然而,没有人会注意到,因为移出的对象是右值,即它没有名称(因此不能以其他方式访问)并且也立即销毁。另一方面,你向世界呈现的新对象根本不是新的,但它仍然是一个非常好的对象(原始对象!)。这是完整的诀窍。

关于你的第二个问题:你的doubleValues函数需要一个std::vector通过不断的引用。这不是一个副本,它与指针一样便宜。但是,然后将所有值复制到一个新对象new_values
然后,您可以通过值返回对象,这意味着你正式做出的第二个副本,最后调用的std::vector赋值运算符,这,其实是连第三副本。

正式,就是这样。优化编译器可能并且希望会优化那些(NRVO)。
曾经是编译器只能做到与未命名的临时对象,但这就像... 10年前。如今,幸运的是,返回值优化通常被称为“正常工作”。

在某些情况下(虽然不在你的例子中),编译器甚至需要来按照最新标准执行copy elision

+0

哇谢谢你!但正如我问geza,“这是否意味着一个空的临时对象被创建后,new_values的内容被复制到临时”? “然后,您按值返回该对象,这意味着您正式创建了第二个副本”因此,您所说的是创建了一个新对象,然后将该函数的返回值复制到临时对象中?我在哪里可以阅读更多关于这个过程(返回值创建临时对象的东西,不知道它是否有名字)? –

+2

@FacPam:是的,理论上这就是必须发生的事情(不是在实践中,希望)。想一想:'new_values'有一个自动存储器,也就是说一个被函数限制的生命周期,这意味着它的成员在堆栈中(不是std :: vector为内部元素分配的内存,而是指向它的内存,结束指针等)。信息将在函数返回后立即“消失”,因为堆栈帧消失。因此,保留(返回)该信息的唯一方法就是复制。或者,_cheating_,这只是复制指针,并且窃取缓冲区。这是...移动。 – Damon

+0

啊,这是一个很好的解释,真的!谢谢:) –