最初の質問
効率上の理由から、ループ外のループ内でのみ使用される複雑なオブジェクト変数を宣言する必要がないようにするための、C ++の洗練されたソリューションはありますか?
詳細な説明
同僚が興味深い点を提起しました。コードポリシーでは、次のように述べています(言い換えると):変数には常に最小限のスコープを使用し、最初の初期化で変数を宣言します。
コーディングガイドの例:
// [A] DO THIS
void f() {
...
for (int i=0; i!=n; ++i) {
const double x = calculate_x(i);
set_squares(i, x*x);
}
...
}
// [B] DON'T do this:
void f() {
int i;
int n;
double x;
...
for (i=0; i!=n; ++i) {
x = calculate_x(i);
set_squares(i, x*x);
}
...
}
これはすべて素晴らしいことであり、プリミティブ型からオブジェクトに移行するまでは、これに問題はありません。(特定の種類のインターフェースの場合)
例:
// [C]
void fs() {
...
for (int i=0; i!=n; ++i) {
string s;
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
...
}
ここで、文字列sは破棄され、ループサイクルごとにメモリが解放され、その後、get_text
関数はsバッファにメモリを新たに割り当てる必要があります。
次のように書く方が明らかに効率的です。
// [D]
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
これで、sバッファに割り当てられたメモリはループの実行間で保持され、割り当てを節約できる可能性が非常に高くなります。
免責事項: 注意:これはループであり、メモリ割り当てについて話しているので、この問題を一般的に考えるのは時期尚早の最適化ではないと思います。確かに、オーバーヘッドが問題にならない場合やループがあります。ただし、開発者が最初に期待するよりもしつこい傾向があり、パフォーマンスが重要なコンテキストでコードが実行される傾向があります。n
とにかく、「一般的な」ループ構造のより効率的な方法は、コードの局所性に違反し、「万が一に備えて」複雑なオブジェクトを場違いに宣言することです。これは私をかなり不安にさせます。
私はそれを次のように書くことを検討していることに注意してください:
// [E]
void fs() {
...
{
string s;
for (int i=0; i!=n; ++i) {
get_text(i, s); // void get_text(int, string&);
to_lower(s);
set_lower_text(i, s);
}
}
...
}
読みやすさがさらに損なわれるため、解決策はありません。
さらに考えてみると、関数のインターフェースはget_text
とにかく非イディオムです。outparamsはとにかく昨日なので、「良い」インターフェースは値で返されます。
// [F]
for (int i=0; i!=n; ++i) {
string s = get_text(i); // string get_text(int);
to_lower(s);
set_lower_text(i, s);
}
ここでは、戻り値からRVOを介して構築される可能性が非常に高いため、メモリ割り当てに2倍の料金を支払うことはありません。したがって、[F]の場合、[C]と同じ割り当てオーバーヘッドを支払います。ただし、[C]の場合とは異なり、このインターフェイスバリアントを最適化することはできません。s
したがって、結論としては、最小限のスコープを使用するとパフォーマンスが低下し、クリーンなインターフェイスを使用すると、少なくとも、out-ref-paramのものが最適化の機会を妨げるよりもはるかにクリーンな値でのリターンを検討します-少なくとも一般的なケースでは。
問題は、効率を上げるためにクリーンなコードを放棄しなければならないほどではありません。問題は、開発者がそのような特殊なケースを見つけ始めるとすぐに、コーディングガイド全体([A]、[B]を参照)が権限を失うことです。 。
今の質問は次のようになります:最初の段落を参照してください