提问人:blizpasta 提问时间:10/3/2008 最后编辑:Coding Mashblizpasta 更新时间:9/8/2012 访问量:843
函数返回另一个函数的返回值
Function returning the return of another function
问:
如果我想调用而不是 ,是否返回 Foo() 返回的副本(额外开销),或者它返回放置在临时堆栈上的相同对象?Bar()
Foo()
Bar()
Foo()
vector<int> Foo(){
vector<int> result;
result.push_back(1);
return result;
}
vector<int> Bar(){
return Foo();
}
答:
2赞
Jorge Ferreira
10/3/2008
#1
通常,它会返回返回的 .但是,这在很大程度上取决于编译器所做的优化。请参阅以下讨论。vector<int>
调试生成
vector<int> Foo(){
004118D0 push ebp
004118D1 mov ebp,esp
004118D3 push 0FFFFFFFFh
004118D5 push offset __ehhandler$?Foo@@YA?AV?$vector@HV?$allocator@H@std@@@std@@XZ (419207h)
004118DA mov eax,dword ptr fs:[00000000h]
004118E0 push eax
004118E1 sub esp,0F4h
004118E7 push ebx
004118E8 push esi
004118E9 push edi
004118EA lea edi,[ebp-100h]
004118F0 mov ecx,3Dh
004118F5 mov eax,0CCCCCCCCh
004118FA rep stos dword ptr es:[edi]
004118FC mov eax,dword ptr [___security_cookie (41E098h)]
00411901 xor eax,ebp
00411903 push eax
00411904 lea eax,[ebp-0Ch]
00411907 mov dword ptr fs:[00000000h],eax
0041190D mov dword ptr [ebp-0F0h],0
vector<int> result;
00411917 lea ecx,[ebp-24h]
0041191A call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (411050h)
0041191F mov dword ptr [ebp-4],1
result.push_back(1);
00411926 mov dword ptr [ebp-0FCh],1
00411930 lea eax,[ebp-0FCh]
00411936 push eax
00411937 lea ecx,[ebp-24h]
0041193A call std::vector<int,std::allocator<int> >::push_back (41144Ch)
return result;
0041193F lea eax,[ebp-24h]
00411942 push eax
00411943 mov ecx,dword ptr [ebp+8]
00411946 call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (41104Bh)
0041194B mov ecx,dword ptr [ebp-0F0h]
00411951 or ecx,1
00411954 mov dword ptr [ebp-0F0h],ecx
0041195A mov byte ptr [ebp-4],0
0041195E lea ecx,[ebp-24h]
00411961 call std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (411415h)
00411966 mov eax,dword ptr [ebp+8]
}
在这里,我们可以看到,在堆栈上创建了一个新对象vector<int> result;
[ebp-24h]
00411917 lea ecx,[ebp-24h]
0041191A call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (411050h)
当我们到达一个新副本时,在调用方分配的存储中创建一个新副本return result;
[ebp+8]
00411943 mov ecx,dword ptr [ebp+8]
00411946 call std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (41104Bh)
并且为位于vector<int> result
[ebp-24h]
0041195E lea ecx,[ebp-24h]
00411961 call std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (411415h)
发布版本
vector<int> Foo(){
00401110 push 0FFFFFFFFh
00401112 push offset __ehhandler$?Foo@@YA?AV?$vector@HV?$allocator@H@std@@@std@@XZ (401F89h)
00401117 mov eax,dword ptr fs:[00000000h]
0040111D push eax
0040111E sub esp,14h
00401121 push esi
00401122 mov eax,dword ptr [___security_cookie (403018h)]
00401127 xor eax,esp
00401129 push eax
0040112A lea eax,[esp+1Ch]
0040112E mov dword ptr fs:[00000000h],eax
00401134 mov esi,dword ptr [esp+2Ch]
00401138 xor eax,eax
0040113A mov dword ptr [esp+8],eax
vector<int> result;
0040113E mov dword ptr [esi+4],eax
00401141 mov dword ptr [esi+8],eax
00401144 mov dword ptr [esi+0Ch],eax
result.push_back(1);
return result;
00401147 push eax
00401148 mov dword ptr [esp+28h],eax
0040114C mov ecx,1
00401151 push esi
00401152 lea eax,[esp+14h]
00401156 mov dword ptr [esp+10h],ecx
0040115A mov dword ptr [esp+14h],ecx
0040115E push eax
0040115F lea ecx,[esp+1Ch]
00401163 push ecx
00401164 mov eax,esi
00401166 call std::vector<int,std::allocator<int> >::insert (401200h)
0040116B mov eax,esi
}
0040116D mov ecx,dword ptr [esp+1Ch]
00401171 mov dword ptr fs:[0],ecx
00401178 pop ecx
00401179 pop esi
0040117A add esp,20h
0040117D ret
该行不调用向量分配器,因为它是在 中的调用站点完成的。优化不会复制 Foo 的结果。vector<int> result
Bar
评论
0赞
PierreBdR
10/3/2008
我确实认为这种解释有点矫枉过正......并且高度依赖编译器。
10赞
PierreBdR
10/3/2008
#2
两者都可能发生。但是,大多数编译器不会在优化后立即执行复制。
您的代码指示应该有一个副本。但是,允许编译器删除任何不更改语义和程序的副本。
注意:这就是为什么你永远不应该有一个复制构造函数,它除了正确复制之外做任何事情,因为你永远无法确定复制是否真的会完成。
评论
0赞
Martin York
10/3/2008
不幸的是,这不是允许编译器删除副本的地方(尽管内联会删除它)。所以是的,向量从 Foo() 复制到 Bar(),然后从 Bar() 复制到调用者。为了使复制 std::vector<> 非常高效,已经做了很多工作。所以别担心。
0赞
Evan Teran
10/3/2008
我认为编译器可以使用返回值优化 (msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx) 安全地将其作为单个副本进行优化。基本上,如果变量将被构造并立即复制,它可以直接构造到要复制的位置。
0赞
PierreBdR
10/4/2008
这绝对是进行优化的地方,即使没有内联。
0赞
blizpasta
10/4/2008
如果这两个函数属于不同的类别,它是否也会得到优化?实际上,这是我想问的实际问题,但忽略了正确提及条件。
2赞
Konrad Rudolph
10/3/2008
#3
对于 NRVO 来说,这是一个微不足道的案例——名称返回值优化(在这种情况下用词不当,因为没有名称)。斯坦·利普曼(Stan Lippman)在一篇博客文章中对所涉及的机制进行了很好的解释。
评论
1赞
David Pierre
10/3/2008
当临时没有命名时,它不就叫 RVO 吗?
1赞
Konrad Rudolph
10/3/2008
@David:是的,有些人,我自己也这样称呼它。但是,技术文献中似乎没有提到“RVO”。“NRVO”被用作包含此内容的技术术语。
评论