[英]C++ return value optimization
這段代碼:
#include <vector>
std::vector<float> getstdvec() {
std::vector<float> v(4);
v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i = 0; i != 1000; ++i)
{
v = getstdvec();
}
}
我在這里的錯誤理解是函數getstdvec不應該實際分配它返回的向量。 當我在valgrind / callgrind中運行它時,我看到有1001個調用malloc; 1表示main中的初始向量聲明,1000表示每次循環迭代。
是什么賦予了? 如何從這樣的函數返回向量(或任何其他對象)而不必每次都分配它?
編輯:我知道我可以通過引用傳遞向量。 我的印象是,有可能(甚至更可取)編寫這樣的函數,它返回一個對象而不會產生不必要的分配。
當您調用函數時,對於像std::vector<T>
這樣的返回類型,編譯器會為返回的對象提供內存。 被調用的函數負責構造它在該內存槽中返回的實例。
現在,RVO / NRVO允許編譯器省略創建本地臨時對象,從中復制構造內存槽中的返回值,破壞臨時對象並最終返回調用者。 相反,被調用的函數只是直接在返回槽的內存中構造本地對象,而在函數的末尾,它只返回。
從調用者的角度來看,這是透明的:它為返回的值提供內存,當返回的函數返回時,有一個有效的實例。 調用者現在可以使用此對象,並負責調用析構函數並在以后釋放內存。
這意味着RVO / NRVO僅在您調用函數來構造新實例時工作,而不是在您分配它時。 以下是可以應用RVO / NRVO的示例:
std::vector<float> v = getstdvec();
但是原始代碼使用循環,並且在每次迭代中,需要構造getstdvec()
的結果,並將此臨時值分配給v
。 RVO / NRVO無法將其刪除。
您可以通過引用傳遞它... copy elision使它成為v = getstdvect()將v(在您的主中)直接分配給v(在您的getstdvec()中)並跳過通常與按值返回相關聯的副本,但是它不會跳過你函數中的v(4)。 為此,您需要通過引用獲取向量:
#include <vector>
void getstdvec(std::vector<float>& v){
v.resize(4);//will only realocate if v is wrong size
v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i=0; i!=1000;++i)
getstdvec(v);
}
你在循環中進行復制賦值,而不是復制構造。 RVO優化僅適用於從返回值構造變量,而不是分配給它們。
我無法弄清楚你在這里要解決的真正問題。 通過更多細節,可以提供解決潛在問題的良好答案。
就目前而言,要以這種方式從函數返回,您需要創建一個臨時向量,以便在每次調用函數時返回。
最簡單的答案是將已創建的矢量對象傳遞給函數。
std::vector<float> getstdvec(std::vector<float> &myvec){
在這種情況下,你真的不必返回它
void getstdvec(std::vector<float> &myvec){
如何從這樣的函數返回向量(或任何其他對象)而不必每次都分配它?
在你的方式中,你聲明一個大小為4的本地向量,所以每次調用該函數時,它將分配內存。 如果您意味着始終在同一向量上進行修改,則可以考慮通過引用傳遞向量。
例如:
void getstdvec(std::vector<float>& vec)
{ //^^
//do something with vec
}
在main
,您聲明向量並按照您的操作分配空間。 您現在執行以下操作:
for (int i=0; i!=1000;++i)
{ //^^^minor: Don't use magic number in the code like this,
//define a const instead
getstdvec(vec);
}
而不是使用返回值,您可以使用參考:
void getstdvec(std::vector<float> &v)
這可以避免臨時對象的復制
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.