質問
次のうち、どちらが良いですか? (c ++に特有)
a。
int i(0), iMax(vec.length());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
//loop body
}
b。
for( int i(0);i < vec.length(); ++i)
{
//loop body
}
長さ関数の呼び出しのため、(a)に対するアドバイスを見ました。これは私を悩ませています。最新のコンパイラは、(b)の最適化を(a)に似たものにしませんか?
解決
例(b)は例(a)とは異なる意味を持ち、コンパイラはユーザーが記述したとおりに解釈する必要があります。
(考えられない何らかの理由で)私がこれを行うためのコードを書いた場合:
for( int i(0);i < vec.length(); ++i)
{
if(i%4 == 0)
vec.push_back(Widget());
}
異なる結果が得られるため、vec.length()の各呼び出しをコンパイラーで最適化することは本当に望まなかったでしょう。
他のヒント
好き:
for (int i = 0, e = vec.length(); i != e; ++i)
もちろん、これはイテレータでも機能します:
for (vector<int>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)
これは、効率的(end()
を1回だけ呼び出す)であると同時に、比較的簡潔(vector<int>::const_iterator
を1回入力するだけ)でもあるため、気に入っています。
誰も明らかなことを言っていないことに驚いています:
99.99%のケースでは、問題ではありません。
size()
の計算が高価な操作であるコンテナを使用していない限り、プログラムが数ナノ秒遅くなることは計り知れません。コードのプロファイルを作成し、<=>がボトルネックであることがわかるまで、読みやすくすることをお勧めします。
ここで議論すべき2つの問題があります:
- 変数スコープ
- 終了条件の再評価
変数スコープ
通常、ループ変数はループの外側で見える必要はありません。それがfor
構造内で宣言できる理由です。
終了条件の再評価
アンドリュー・シェパードは、それをうまく述べました:それは、関数呼び出しを終了条件の中に入れることとは何か違うことを意味します:
for( vector<...>::size_type i = 0; i < v.size(); ++i ) { // vector size may grow.
if( ... ) v.push_back( i ); // contrived, but possible
}
// note: this code may be replaced by a std::for_each construct, the previous can't.
for( vector<...>::size_type i = 0, elements = v.size(); i != elements; ++i ) {
}
なぜあなたを悩ませているのですか? これらの2つの選択肢は同じことをしているとは思わない。 1つは固定数の反復を実行し、もう1つはループ本体に依存しています。
もう1つの代替colud be
for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
//loop body
}
ループ外のループ変数が必要な場合を除き、2番目の方法が望ましいです。
イテレータは、実際には良いまたはより良いパフォーマンスを提供します。 (comp.lang.c ++。moderatedには数年前に大きな比較スレッドがありました。)
また、私は使用します
int i = 0;
使用している構文のようなコンストラクタではなく。有効ではありますが、慣用的ではありません。
やや無関係:
警告:符号付き整数と符号なし整数の比較。
配列およびベクトルインデックスの正しいタイプは size_t です。
厳密に言えば、C ++ではstd::vector<>::size_type
です。
C / C ++の開発者の数がまだこれを間違えているのは驚くべきことです。
生成されたコードを見てみましょう(完全に最適化されたMSVS 2008を使用しています)。
a。
int i(0), iMax(vec.size());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
//loop body
}
forループは2つのアセンブラー命令を生成します。
b。
for( int i(0);i < vec.size(); ++i)
{
//loop body
}
forループは、8つのアセンブラー命令を生成します。 vec.size()は正常にインライン化されました。
c。
for (std::vector<int>::const_iterator i = vec.begin(), e = vec.end(); i != e; ++i)
{
//loop body
}
forループは15個のアセンブラー命令を生成します(すべてがインライン化されていますが、コードには多くのジャンプがあります)
したがって、アプリケーションがパフォーマンスに重要な場合は、a)を使用します。それ以外の場合b)またはc)。
イテレータの例に注意してください:
for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
//loop body
}
ループイテレータ 'it'を無効にすると、ループ本体によってベクトルが再割り当てされます。したがって、それは同等ではありません
for (int i=0;i<vec.size();++i){
//loop body
}
whereループ本体は要素をvecに追加します。
単純な質問:ループ内でvec
を変更しますか?
この質問への回答はあなたの回答にもつながります。
jrh
インライン化されない限り、コンパイラがvec.length()
呼び出しを定数であるという安全な知識で引き上げるのは非常に困難です(願わくばそうなることが多いでしょう!)。ただし、i
呼び出しを<!> quot; manually <!> quot;にする必要がある場合でも、少なくともlength
は必ず2番目のスタイル<!> quot; b <!> quot;で宣言する必要があります。ループから引き上げられました!
これが望ましい:
typedef vector<int> container; // not really required,
// you could just use vector<int> in for loop
for (container::const_iterator i = v.begin(); i != v.end(); ++i)
{
// do something with (*i)
}
- すぐにベクトルが 更新されていません
- 誰でも何が起きているのかわかる ここ
- ループの数を知っています
-
v.end()
は、1つ先のポインターを返します 最後の要素なので、オーバーヘッドはありません サイズ確認の - さまざまなアップデートが簡単 コンテナまたは値タイプ
(b)は毎回関数を計算/呼び出ししません。
-抜粋を開始----
ループ不変コードの動き: GCCには、ループオプティマイザーの一部として、また部分的な冗長性除去パスにループ不変コードモーションが含まれています。この最適化により、ループから命令が削除され、ループの存続期間を通じて変化しない値が計算されます。
---抜粋を終了-
gccのその他の最適化:
https://www.in.redhat.com/software /gnupro/technical/gnupro_gcc.php3
#include <boost/foreach.hpp>
std::vector<double> vec;
//...
BOOST_FOREACH( double &d, vec)
{
std::cout << d;
}