2013-10-18 15 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でこれを実行すると、mallocに1001の呼び出しがあることがわかります。 mainの初期ベクトル宣言では1、すべてのループ反復では1000です。

何がありますか?どのように私は毎回それを割り当てることなく、このような関数からベクトル(または他のオブジェクト)を返すことができますか?

編集:私は参考にベクトルを渡すことができます。私は、不要な割り当てを発生させることなくオブジェクトを返すような、このような関数を書くことが可能であった(そしてさらに好ましい)という印象を受けていました。

+0

あなたの編集のために、参考にならない解決策を提供するために、この極小のサンプルコードではなく、解決しようとしている問題の実際の例が必要です。 –

+0

@マークB、それは本当に単純です:私は不要なコピー/割り当てを行うことなくベクトルを返す関数が必要です。私は、RVOまたは右辺値に関連する何かがこの非常に簡単なことを可能にする印象を受けました。現実世界の単純な例は、ベクトルyとx、スカラーkに対してy = k * xを実行しようとしています。従来のpass-by-reference関数は 'void mult(const float&k、const vec&x、vec&y)'のようになります。しかし明らかに関数呼び出し 'y = mult(k、x)'は 'mult(k、x、y)'よりも好ましい。 – Aurelius

+1

RVO(戻り値の最適化)は、コンパイラがコードに対して行う処理です。コードでは、まず最適化できるものを実行する必要があります(一時的なものを渡して、同じオブジェクトに戻すなど)。あなたはそのコードを見て、おそらく、私はgetstdvecへの参照を渡すことで最適化できると考えました。なぜコンパイラーはそれをしないのですか?さて、参照を渡すことはあなたのコードに暗示されていません。コンパイラは、コードが行うことを最適化することしか期待できません。 – iheanyi

答えて

1

最も単純な答えは、既に作成されたベクトルオブジェクトを関数に渡すことです。あなたは本当にあなたが参照によって渡すことができますので、

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

それを返す必要はありません。その場合

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

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のローカルベクトルを宣言し、その関数が呼び出されるたびに、メモリを割り当てるために起こっています。同じベクトル上で常に変更することを意味する場合は、代わりに参照渡しを考慮する必要があります。例えば

main内部

void getstdvec(std::vector<float>& vec) 
{        //^^ 
    //do something with vec 
} 

、あなたはベクトルを宣言し、あなたが何をしたかのようにスペースを割り当てます。これで、次の操作を行います。代わりに

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がこれを取り除く方法はありません。