Функция, возвращающая возврат другой функции

Если я хочу вызвать Bar() вместо Foo(), возвращает ли Bar() копию (дополнительные накладные расходы) того, что возвращает Foo (), или он возвращает тот же объект, который Foo() помещает во временный стек?

vector<int> Foo(){  
    vector<int> result;  
    result.push_back(1);  
    return result;  
}  
vector<int> Bar(){  
    return Foo();  
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
809
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Обычно он возвращает копию возвращенного 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

Линия vector<int> result не вызывает распределитель векторов, потому что это выполняется на месте вызова в Bar. Оптимизация не копирует результат Foo.

Я действительно думаю, что это объяснение немного излишне ... И сильно зависит от компилятора.

PierreBdR 03.10.2008 14:32
Ответ принят как подходящий

И то, и другое может случиться. Однако большинство компиляторов не будет копировать, как только вы оптимизируете.

Ваш код указывает, что должна быть копия. Однако компилятору разрешено удалить любую копию, не изменяющую семантику и программу.

Примечание: вот почему вам НИКОГДА не следует иметь конструктор копирования, который делает что-либо, кроме правильного копирования, поскольку вы никогда не можете быть уверены, будет ли копия действительно сделана или нет.

К сожалению, это не то место, где компилятору разрешено удалять копию (хотя встраивание удалит ее). Итак, да, вектор копируется из Foo () в Bar (), а затем копируется из Bar () вызывающей стороне. Была проделана большая работа, чтобы сделать копирование std :: vector <> очень эффективным. Так что не волнуйтесь.

Martin York 03.10.2008 18:43

Я думаю, что компилятор может безопасно оптимизировать это как единую копию, используя оптимизацию возвращаемого значения (msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx). В принципе, если переменная будет создана и немедленно скопирована, ее можно просто построить прямо туда, где она будет скопирована.

Evan Teran 03.10.2008 19:31

Это абсолютно то место, где происходит оптимизация, даже без встраивания.

PierreBdR 03.10.2008 22:45

Будет ли он оптимизирован, если две функции находятся в разных классах? На самом деле это то, о чем я хотел спросить, но не упомянул условия должным образом.

blizpasta 04.10.2008 01:53

Это тривиальный случай для оптимизации возвращаемого значения NRVO - имена (неправильное употребление в данном случае, поскольку имени нет). Стэн Липпман имеет запись в блоге с хорошим объяснением задействованного механизма.

Разве это не просто RVO, когда временное не было названо.

David Pierre 03.10.2008 14:50

@David: Да, некоторые, и я сам называл это так. Однако в технической литературе, похоже, нет упоминания о «РВО». «NRVO» используется как технический термин, охватывающий это.

Konrad Rudolph 03.10.2008 15:33

Другие вопросы по теме