一般的な質問として、関数のシグネチャは次のとおりです。
void f( const string & s )...
実際に文字列を変更していないのに、なぜこれを参照渡しする必要があるのでしょうか (文字列は定数であるため)。
一般的な質問として、関数のシグネチャは次のとおりです。
void f( const string & s )...
実際に文字列を変更していないのに、なぜこれを参照渡しする必要があるのでしょうか (文字列は定数であるため)。
参照渡しには、ポインター渡しと値渡しの 2 つの方法があります。
ポインターによる受け渡しは、参照による受け渡しと似ています。「ポインターを変更したくないのになぜポインターを渡すのか」という同じ議論がそれに対して行われる可能性があります。
値渡しには、コピーを作成する必要があります。文字列のコピーは通常、動的メモリの割り当てと割り当て解除を裏で行う必要があるため、よりコストがかかります。const
そのため、変更する予定のないコピーを渡すよりも、参照/ポインターを渡す方がよい場合がよくあります。
変数を値で渡すと、コピーが作成されます。参照渡しにより、そのコピーが回避されます。マークを付けたいconst
のは同じ理由です。コピーを作成していないので、誤ってオリジナルを台無しにしたくありません。これにより、コンパイラの最適化も可能になる可能性があると思います。
int
、char
、 、およびその他のプリミティブ型でこれが通常見られない理由はfloat
、コピーするのが比較的安価であり、場合によっては、参照渡しのほうがコストがかかるからです (たとえば、char
参照渡しでは 64 を渡す必要がある場合があります)。 8 ビットの代わりに - ビットのデータ (ポインタ) 参照渡しはまた、文字列のような大きな型では大したことではありませんが、int
.
「必要」ではありませんが、一般的な答えは「パフォーマンス上の理由から、コピーを防ぐため」ですが、それは素朴な答えであり、真実はもう少し複雑です。
s
あなたの例では、実際には不変であり、「所有していない、または変更できない」ものであると仮定すると、const
デコレータはs
. の参照がs
取得されなかった場合は、コピーが保証されます (コンパイラの最適化を除く)。
返品後f()
のコピーを使用しない場合、コピーの労力が無駄になります。そのため、参照渡しによりコピーが防止され、文字列を検査する機能が保持されます。偉大な。繰り返しますが、それは素朴な答えであり、C++ 11より前のものがほとんど正しい答えです。s
f()
s
f()
s
「それが必要か?」に答えるために検討する価値のあるシナリオは他にもあります。しかし、私は1つだけに焦点を当てます:
の呼び出し元が の呼び出し後に
f()
文字列を必要としないが、データのコピーを保持する必要がある場合。s
f()
f()
コードは次のとおりです。
void f(const std::string& arg1); // f()'s signature
void g(const std::string& arg1) {
std::string s(arg1);
s.append(" mutate s");
f(s);
}
この場合、 string を構築し、参照s
によって に渡します。 が不透明であり、それ以上の最適化が利用できないと仮定すると、パフォーマンスの観点からはすべて問題ありません。const
f()
f()
f()
では、 のデータのコピーが必要だとs
します。さて、f()
コピー コンストラクターを呼び出しs
て、ローカル変数にコピーします。
// Hypothetical f()
void f(const std::string& s) {
this->someString_ = s;
}
この場合、データが に格納されるまでにsomeString_
、 では通常のコンストラクタが呼び出されg()
、 ではコピー クタが呼び出されますがf()
、 で行われた作業はg()
無駄になります。パフォーマンスを向上させるには、値渡しおよび/またはムーブ コンストラクターの使用という 2 つの方法があります。
// Explicitly move arg1 in to someString_
void f(std::string&& arg1) {
this->somestring_ = std::move(arg1);
}
void g(const std::string& arg1) {
std::string s(arg1);
s.append(" mutate s");
f(s);
}
これは、コンパイラが自動的に から開始することを明示的に行っていますC++11
。つまり、より正しいバージョンは、値で渡し、コンパイラに正しいことをさせることです。
void f(std::string arg1) {
this->somestring_ = arg1; // Implicit move, let the compiler do the right thing
}
void g(const std::string& arg1) {
std::string s(arg1);
s.append(" mutate s");
f(s);
}
この場合、文字列は で構築されg()
、追加の作業はどこにも行われません。したがって、この場合の答えは、
実際に文字列を変更していないのに、なぜこれを参照渡しする必要があるのでしょうか (文字列は定数であるため)。
文字列は変更されていませんが、コピーされているため、const 参照は必要ありませんでした。
s
への呼び出し後に変更されている場合と変更されていない場合に、コンパイラが実行できる最適化を一覧表示するのは、読者の課題f()
です。
David Abrams の投稿「スピードが欲しいですか? 値渡し' またはC++ では、値渡しと定数参照渡しのどちらが優れていますか? C++ でオブジェクトを関数に渡す方法を投稿しますか? .