10

このコード:

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

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

編集:ベクトルを参照渡しするだけでよいことはわかっています。不必要な割り当てを発生させることなく、オブジェクトを返すこのような関数を作成することは可能である (さらには望ましいことである) という印象を受けました。

4

6 に答える 6

23

関数を呼び出すとstd::vector<T>、コンパイラのような戻り値の型に対して、返されたオブジェクトにメモリが提供されます。呼び出された関数は、このメモリ スロットに返すインスタンスを構築する責任があります。

RVO/NRVO により、コンパイラはローカルの一時オブジェクトの作成を省略できるようになり、そこからメモリ スロットに返された値をコピー構築し、一時オブジェクトを破棄し、最終的に呼び出し元に戻ります。代わりに、呼び出された関数は戻りスロットのメモリにローカル オブジェクトを直接構築し、関数の最後で戻ります。

呼び出し元の観点からは、これは透過的です。戻り値にメモリを提供し、呼び出された関数が返されると、有効なインスタンスが存在します。呼び出し元はこのオブジェクトを使用できるようになり、後でデストラクタを呼び出してメモリを解放する責任があります。

つまり、RVO/NRVO は、関数を呼び出して新しいインスタンスを構築する場合にのみ機能し、割り当てた場合には機能しません。以下は、RVO/NRVO を適用できる場所の例です。

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

ただし、元のコードはループを使用し、各反復で からの結果をgetstdvec()構築する必要があり、この一時的なものは に割り当てられvます。RVO/NRVO がこれを取り除く方法はありません。

于 2013-10-18T16:13:41.917 に答える
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);
}
于 2013-10-18T16:08:22.340 に答える
2

コピー構築ではなく、ループでコピー割り当てを行っています。RVO 最適化は、戻り値からの変数の構築にのみ適用され、変数への割り当てには適用されません。

ここであなたが解決しようとしている本当の問題がよくわかりません。詳細があれば、根本的な問題に対処する適切な回答を提供できる可能性があります。

現状では、そのような方法で関数から戻るには、関数が呼び出されるたびに戻る一時的なベクトルを作成する必要があります。

于 2013-10-18T16:09:57.137 に答える
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);
}
于 2013-10-18T16:09:16.170 に答える
1

最も簡単な答えは、作成済みのベクター オブジェクトを関数に渡すことです。

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

その場合は特に返却する必要はありませんので、

void getstdvec(std::vector<float> &myvec){
于 2013-10-18T16:08:16.173 に答える
1

戻り値を使用する代わりに、参照を使用できます。

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

一時オブジェクトのコピーを回避できるもの

于 2013-10-18T16:09:45.110 に答える