2013-10-18 60 views
9

此代碼:C++返回值優化

#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次。

什麼給?我怎樣才能從這樣的函數中返回一個矢量(或任何其他對象),而不必每次都分配它?

編輯:我知道我可以通過引用傳遞向量。我的印象是,有可能(甚至最好)編寫這樣的函數,返回一個對象而不會產生不必要的分配。

+0

對於您的編輯:我們需要一個您正試圖解決的問題的實例,而不是這個極其微小的示例代碼,以幫助提供非通過參考的解決方案。 –

+0

@MarkB,它的確如此簡單:我想要一個返回矢量而不必進行不必要的複製/分配的函數。我的印象是,與RVO或rvalues有關的東西會使這件事情變得非常簡單。一個簡單的現實世界的例子就是嘗試對向量y和x以及標量k做y = k * x。傳統的傳遞參考函數看起來像'void mult(const float&k,const vec&x,vec&y)'。但顯然函數調用'y = mult(k,x)'比'mult(k,x,y)'更可取。 – Aurelius

+1

RVO(返回值優化)是編譯器對您的代碼執行的操作。你的代碼需要首先做一些可以優化的東西(例如傳遞一個臨時的東西,例如分配給同一個對象)。你可能已經看過那個代碼並且認爲 - 嗯,我可以通過傳遞一個對getstdvec的引用來優化它。爲什麼編譯器不這樣做?那麼,傳遞參考並不代表你的代碼。你只能期望編譯器優化你的代碼所做的事情 - 而不是它可以做的事情。 – iheanyi

回答

1

最簡單的答案是將已創建的矢量對象傳遞給函數。

std::vector<float> getstdvec(std::vector<float> &myvec){ 

在這種情況下,你真的沒有返回這麼

void getstdvec(std::vector<float> &myvec){ 
3

您可以通過引用傳遞它...複製省略使得它使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); 
} 
1

我怎樣才能返回從這樣的功能的載體(或任何其他對象),而不必分配它的每一個時間?

在你的方式,你聲明大小爲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); 
} 
1

在代替使用返回值,你可以使用一個參考:

void getstdvec(std::vector<float> &v) 

可避免臨時對象

2

你做副本的副本在循環中分配,而不是複製。 RVO優化僅適用於從返回值構造變量,而不是分配給它們。

我不能完全弄清楚你在這裏試圖解決的真正問題。有了更多的細節,就有可能提供一個解決您的潛在問題的良好答案。按照這種方式返回函數,您需要創建一個臨時向量,以在每次調用該函數時返回。

15

當您調用函數時,對於返回類型如std::vector<T>,編譯器會爲返回的對象提供內存。被調用的函數負責構造它在這個內存槽中返回的實例。

RVO/NRVO現在允許編譯器省略創建本地臨時對象,從內存槽中複製構造返回值,破壞臨時對象並最終返回給調用者。相反,被調用函數直接在返回槽的內存中直接構造本地對象,並在函數結束時返回。

從調用者的角度來看,這是透明的:它爲返回的值提供內存,並且當調用的函數返回時,會有一個有效的實例。調用者現在可以使用這個對象,並且負責調用析構函數並在以後釋放內存。

這意味着RVO/NRVO僅適用於當您調用函數構造新實例時,而不是在您分配它時。以下是其中可以應用RVO/NRVO一個例子:

std::vector<float> v = getstdvec(); 

但原始代碼使用循環和在每次迭代中,需要構建從getstdvec()結果和該臨時分配給v。 RVO/NRVO無法解決這個問題。