12

通常、次のような 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 の範囲を制限する可能性以外に、最初のスタイルの長所はありますか?

4

13 に答える 13

14

コードを適切に行にラップすると、インライン フォームも同様に読みやすくなります。iterEnd = container.end()さらに、最適化として常に次のことを行う必要があります。

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

更新: paercebal のアドバイスに従ってコードを修正しました。

于 2008-10-09T20:29:28.123 に答える
9

もう 1 つの方法は、boost foreachなどの foreach マクロを使用することです。

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

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

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

于 2008-10-09T20:43:50.827 に答える
5

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

どちらの方法でも効率が向上するかどうかは真剣に疑っています。また、typedef を使用して読みやすくすることもできます。

typedef SomeClass::SomeContainer::iterator MyIter;

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

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

于 2008-10-09T20:25:40.083 に答える
4

-O2最適化でg ++でこれを見た(具体的に)

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

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

于 2008-10-11T16:10:48.227 に答える
4

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

時期尚早の最適化は諸悪の根源です。

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

于 2008-10-11T16:02:01.130 に答える
1

悪いスタイルではないと思います。typedefを使用して、STLの冗長性と長い行を回避します。

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

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

スパルタンプログラミングは、スタイルの問題を軽減する1つの方法です。

于 2008-10-12T04:17:45.390 に答える
1

コードがバラバラになるかもしれませんが、別の関数に引き出して、両方のイテレータを渡すことも好きです。

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

そして持っている..

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

好きなこと:

  • 見苦しいイテレータ型について言及する必要はありません (または、それが const か not-const かを考えてください)。
  • 各反復で end() を呼び出さないことで利益が得られる場合は、それを取得しています

嫌いなこと:

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

しかし、いつの日か、ラムダが登場するでしょう!

于 2008-10-09T21:19:32.283 に答える
1

どちらかというと特に強い意見はありませんが、イテレータの有効期間を考慮すると、対象スコープのバージョンに傾倒するでしょう。

ただし、読みやすさが問題になる場合があります。これは、typedef を使用することで解決できるため、反復子の型はもう少し扱いやすくなります。

typedef SomeClass::SomeContainer::iterator sc_iter_t;

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

大きな改善ではありませんが、少しです。

于 2008-10-09T20:30:44.953 に答える
1

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

これはコンソールでは当てはまらない場合がありますが、ループをアンアセンブルして、最適化が行われているかどうかを確認できます。そうである場合は、好みのスタイルまたは組織の標準であるスタイルを選択できます。

于 2008-10-09T20:43:20.670 に答える
0

スコープが気になる場合は、初期化とループの周りに中かっこを投げることができます。多くの場合、関数の先頭で反復子を宣言し、プログラム全体で反復子を再利用します。

于 2008-10-09T20:27:35.053 に答える
0

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

于 2008-10-09T20:27:55.257 に答える
0

私はフェルッチオに同意します。end() 呼び出しをループから引き出すために、最初のスタイルを好む人もいるかもしれません。

また、C++0x を使用すると、実際には両方のバージョンがよりクリーンになることも付け加えておきます。

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

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}
于 2008-10-09T20:28:07.533 に答える
0

私は通常、次のように書きます。

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

for(...)
于 2008-10-09T20:29:08.420 に答える