簡體   English   中英

C ++返回值優化

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM