forループ内のイテレータの初期化は不適切なスタイルと見なされますか?

StackOverflow https://stackoverflow.com/questions/189055

質問

通常、次のようなSTLコードがあります。

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

ただし、実際には次のように記述することをお勧めします。

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
for (; Iter != IterEnd; ++Iter)
{
}

スコーピングが心配な場合は、括弧を追加してください:

{
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
    for (; Iter != IterEnd; ++Iter)
    {
    }
}

これは、特にコンソールをプログラミングしている場合、ループの各反復で.end()関数が呼び出されないため、速度と効率が向上するはずです。私はパフォーマンスの改善を当然のことと考えていますが、それは妥当なように見えますが、どのくらいかはわかりませんし、コンテナのタイプと実際に使用されているSTL実装に依存します。しかし、このスタイルを数か月使用したので、とにかく最初のスタイルよりも実際に好みます。

読みやすさの理由:for行はきちんと整理されています。実際のプロダクションコードで修飾子とメンバー変数を使用すると、最初の例のスタイルを使用する場合、行に本当に長くするのは非常に簡単です。それが、この例で意図的に水平スクロールバーを持つようにした理由です。ちょうど私が話していることを見るためです。 ;)

一方、forループの外側のスコープに突然Iter変数を導入します。しかし、少なくとも作業環境では、最初の例でも外部スコープでIterにアクセスできたはずです。

これについてどう思いますか?おそらくIterの範囲を制限する以外に、最初のスタイルのプロはいますか?

役に立ちましたか?

解決

コードを適切に行にラップすると、インライン形式も同様に読み取り可能になります。また、最適化として常に iterEnd = container.end()を実行する必要があります。

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
    IterEnd = m_SomeMemberContainerVar.end();
    Iter != IterEnd;
    ++Iter)
{
}

更新:掲示板のアドバイスごとにコードを修正しました。

他のヒント

別の方法として、foreachマクロを使用する方法があります。たとえば、 boost foreach

BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
   mangle( item );
}

マクロは現代のc ++では推奨されていませんが、autoキーワードが広く利用できるようになるまで、これは簡潔で読みやすく、しかも完全にタイプセーフで高速なものを見つけるための最良の方法です。初期化スタイルを使用してマクロを実装すると、パフォーマンスが向上します。

リンクされたページには、迷惑なすべての大文字を避けるために、foreachとしてBOOST_FOREACHを再定義することに関するメモもあります。

最初の形式(forループ内)は、forループの後に反復子が必要ない場合に適しています。スコープをforループに制限します。

どちらの方法でも効率が上がることを真剣に疑います。 typedefで読みやすくすることもできます。

typedef SomeClass::SomeContainer::iterator MyIter;

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

短い名前をお勧めします;-)

g ++で-O2最適化で(これは具体的なものとして)これを見た

std :: vector、std :: list、およびstd :: map(およびフレンド)の生成コードに違いはありません。 std :: dequeには小さなオーバーヘッドがあります。

したがって、一般的に、パフォーマンスの観点からはほとんど違いはありません。

いいえ、ループが始まる前に iter.end()を保持するのは悪い考えです。ループがコンテナを変更すると、終了イテレータが無効になる場合があります。また、 end()メソッドはO(1)であることが保証されています。

早すぎる最適化はすべての悪の根源です。

また、コンパイラはあなたが思っているよりも賢いかもしれません。

イテレータの有効期間は、対象範囲のバージョンに私を傾かせますが、私は何らかの方法で特に強い意見はありません。

ただし、読みやすさが問題になる場合があります。 typedefを使用すると、イテレータの型がもう少し管理しやすくなります:

typedef SomeClass::SomeContainer::iterator sc_iter_t;

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

大きな改善ではなく、少し。

コンソールの経験はありませんが、最新のC ++コンパイラのほとんどでは、スコープの問題を除いて、どちらのオプションも同等のものになります。 Visual Studioコンパイラーは、デバッグコードでも事実上常に、暗黙的な一時変数(通常はレジスター)に条件比較を配置します。そのため、論理的には各反復でend()呼び出しが行われているように見えますが、最適化されたコンパイル済みコードは実際に呼び出しを1回だけ行い、比較はループの各副次的な時間で行われます。

これはコンソールでは当てはまらないかもしれませんが、ループを逆アセンブルして、最適化が行われているかどうかを確認できます。そうであれば、好みのスタイルや組織の標準的なスタイルを使用できます。

コードがばらばらになる可能性がありますが、別の関数に引き出して、両方のイテレータを渡すことも好きです。

doStuff(coll.begin(), coll.end())

and have ..

template<typename InIt>
void doStuff(InIt first, InIt last)
{
   for (InIt curr = first; curr!= last; ++curr)
   {
       // Do stuff
   }
 }

好きなこと:

  • イテレータのイテレータの種類に言及する必要はありません(または、constかどうかを考える必要はありません)
  • 各反復でend()を呼び出さないことで利益が得られる場合、それを取得しています

気に入らないこと:

  • コードを分割します
  • 追加の関数呼び出しのオーバーヘッド。

しかし、いつか、ラムダができます!

まったく悪いスタイルだとは思わない。 typedefを使用して、STLの冗長性と長い行を避けます。

typedef set<Apple> AppleSet;
typedef AppleSet::iterator  AppleIter;
AppleSet  apples;

for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
   ...
}

Spartanプログラミングは、スタイルに関する懸念を軽減する1つの方法です。

スコープについて懸念がある場合は、初期化とループに中括弧を投げることができます。多くの場合、関数の開始時にイテレータを宣言し、プログラム全体でイテレータを再利用します。

Ferruccioに同意します。 end()呼び出しをループから引き出すために、最初のスタイルが好まれる場合があります。

また、C ++ 0xが実際に両方のバージョンをよりクリーンにすることも追加できます。

for (auto iter = container.begin(); iter != container.end(); ++iter)
{
   ...
}

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}

通常は次のように書きます:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
                                   IterEnd = m_SomeMemberContainerVar.end();

for(...)

1つの巨大な行で終わるわけではないので、2番目のオプションの方が読みやすいと思います。ただし、Feruccioはスコープについて良い点を挙げています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top