2011-02-17 5 views
4

STLコンテナを返す関数を持っている場合、標準コンテナの内容全体をコピーする必要がありますか?標準コンテナを返却すると、コンテナの内容のコピーが発生しますか?

これは次のようになります。これより

void Foo(std::vector<std::string>* string_list); 

よりよい:

std::vector<std::string> Foo(); 

容器の中に何があるかは関係していますか?たとえば、次のようにコンテナを返します:

struct buzz { 
    int a; 
    char b; 
    float c; 
} 

std::map< int, buzz > Foo(); 

は、これよりもより高価な操作である:

std::map< int, int > Foo(); 

おかげで、 PaulH


編集: これはですC++ 03。残念ながら、C++ 0xソリューションは受け入れられません。

Edit2: 私はMicrosoft Visual Studio 2008コンパイラを使用しています。

+0

もっと重要なことは重要ですか?いいえ、* clean *と* straightforward *のコードを書いておけば、プロファイラを使って遅い部分が何であるかを知ることができます。関数からの復帰が大量の時間を費やしている場合は、パフォーマンスのためにコードを難読化するだけの価値があります。 – GManNickG

答えて

6

C++ 03はおそらく戻り値の最適化(Google RVOとNRVO)を行います。

この最適化が適用されない場合、C++ 0xはmove semanticsを実行します。

2

はい、コンテナのコピーが含まれますが、void Foo(std::vector<std::string>* string_list);は使用しないでください。代わりにvoid foo(vector<string>& string_list);を使用してください。

または単にC++ 0xに切り替えて、ライブラリ内に既に最適化を実装しているコンパイラを使用してください。

+4

コピーが発生していない可能性があります。実際、ほとんどの場合そうではありません。それは本当に関数の内容に依存します。 –

+1

なぜ参照セマンティクスが好きですか?私は関数内で変更しようとしている値のポインタバイパスメソッドを常に好んでいました。そのため、関数を呼び出すときにユーザーがその小さな&を入れなければなりません。彼らが私の機能が価値を変えることを理解していることを示すように。 – PaulH

+0

ポインタが意味的に弱いため、コンパイラはクリアコードエラーを無視できます。もう一つの大きな問題は、ポインタを介してconstを伝播することです(大きな痛み)。 –

3

私は100%で確認されませんでしたが、NO(コメンテーターのおかげで):(LOCAL_FUN付き)

#include <vector> 
#include <iostream> 

#define LOCAL_FUN 

struct A { 
    A() { std::cout << "default ctor" << std::endl; } 
    A(const A &a) { std::cout << "copy ctor" << std::endl; } 
}; 

#ifdef LOCAL_FUN 
std::vector<A> *pVec = NULL; 
#endif 

std::vector<A> func() 
{ 
    std::vector<A> vec; 
#ifdef LOCAL_FUN 
    pVec = &vec; 
#endif 
    vec.push_back(A()); 
    std::cout << "returning" << std::endl; 
    return vec; 
} 

int main(int argc, char *argv[]) 
{ 
    std::vector<A> ret = func(); 
#ifdef LOCAL_FUN 
    if (pVec) { 
     std::cout << pVec->size(); 
    } 
#endif 
} 

出力:

default ctor 
copy ctor 
returning 
1 

編集:いくつかのより多くの主導のコードで遊んで私はローカル変数(LOCAL_FUN)でいくつかの楽しみに。コピーを最適化しない本当に悪いコンパイラは、このコードを実際に破ることができます。

+2

コピーは 'push_back'で行われ、**は返されません**。実際、あなたのコードでは、返す間に* no *コピーが行われることが示されています。 –

+0

ああ、私は「いいえ」と書こうと思った。 –

+0

これは最も可能性が高い結果ですが、 "コピーctor"を2回(または3回)表示すると出力が正しいことになり、壊れたコンパイラを意味するわけではなく、単に最適化が失敗したことを意味します。 – aschepler

0

最初のコンパイラでコピー構成を削除する必要があります。コピーできない場合は移動し、コピーできない場合は移動してください。したがって、コンパイラが本当に悪い場合は、余分なコピーのオーバーヘッドが発生する危険性があります。詳細については、this discussionを参照してください。

+0

コピーが削除されるという_requirement_はありません。いくつかの状況下では、コンパイルによってコピーが削除される可能性があります。 –

+0

Elisionは必要ありません。インプリメンテーションが最初にエリシオンを「試行」する必要はありません。 –

+0

@James McNellis、@Fred Nurk - あなたは私の返事を間違って解析しているかもしれません。私は、コンパイラが必要とするコンパイラについて言いたいわけではありません。コピーする前にシーケンスコンパイラを実行するだけです。 –

0

コンテナのコピーコンストラクタによって異なります。 C++には値渡しのセマンティクスがあります。したがって、関数Foo()のベクトルを返すと、それは値セマンティクスを使用して返されます。つまり、ベクトルの値をコピーするためにコピーコンストラクタが呼び出されます。この場合、std :: vectorのコピーコンストラクタは新しいコンテナを作成し、その値をコピーします。コンテナにポインタを渡す場合は、すでに割り当てられていない場合はメモリを割り当てる必要があります。そのため、ポインタはnull値ではなく実際のコンテナを指しています。プログラミングのプラクティスの観点からは、セマンティクスを解釈のために開いたままにするため、これは良いことではありません。より良い考え方は、コンテナへの参照を渡して、関数が所望の要素でコンテナを満たすようにすることです。

+1

これは当てはまりません。あなたの答えが間違っている理由についてFredの答えを見てください。 –