35

私は最近 C++11 をますます使用していることに気付き、以前は反復子を使用していた場所で、可能な限り範囲ベースの for ループを使用しています。

std::vector<int> coll(10);
std::generate(coll.begin(), coll.end(), []() { return rand(); } );

C++03:

for (std::vector<int>::const_iterator it = coll.begin(); it != coll.end(); ++it) {
   foo_func(*it);
}

C++11:

for (auto e : coll) { foo_func(e); }

しかし、コレクション要素の型がテンプレート パラメーターの場合はどうなるでしょうか。foo_func()おそらくオーバーロードされて、複雑な (= コピーにコストがかかる) 型を const 参照で渡し、単純なものを値で渡します:

foo_func(const BigType& e) { ... };
foo_func(int e) { ... };

上記の C++03 スタイルのコードを使用している間は、これほど深く考えたことはありませんでした。私は同じように反復し、const_iterator を逆参照すると const 参照が生成されるため、すべて問題ありませんでした。しかし、C++11 の範囲ベースの for ループを使用すると、const 参照ループ変数を使用して同じ動作を得る必要があります。

for (const auto& e : coll) { foo_func(e); }

autoそして突然、これが単純な型 (参照を実装するための舞台裏のポインターなど) である場合に不要なアセンブリ命令が導入されないかどうか、もう確信が持てなくなりました。

しかし、サンプル アプリケーションをコンパイルすると、単純な型にはオーバーヘッドがなく、これがテンプレートで範囲ベースの for ループを使用する一般的な方法であることが確認されました。そうでない場合は、boost::call_traits::param_typeが適していたでしょう。

質問:規格に保証はありますか?

(この問題は、実際には範囲ベースの for ループとは関係がないことを認識しています。const_iterators を使用している場合にも発生します。)

4

2 に答える 2

15

標準のコンテナはすべて、イテレータから参照を返します(ただし、一部のコンテナは実際にはコンテナではないことに注意してください。たとえば、std::vector<bool>プロキシを返します)。厳密にはサポートされていませんが、他のイテレータはプロキシまたは値を返す場合があります。

もちろん、この規格はパフォーマンスに関していかなる保証も行いません。(複雑さの保証を超えた)あらゆる種類のパフォーマンス関連機能は、実装の品質と見なされます。

そうは言っても、以前と同じようにコンパイラーに選択を任せることを検討することをお勧めします。

for (auto&& e: coll) { f(e); }

ここでの主な問題は、非参照f()を受け取る可能性があることです。これは、必要に応じてのバージョンをconst使用して防ぐことができます。constcoll

于 2012-10-24T21:13:53.203 に答える
11

6.5.4 / 1は言う:

for ( for-range-declaration : braced-init-list ) statement

range-initをbraced-init-listと同等にします。いずれの場合も、範囲ベースのforステートメントは次のようになります。

{
    auto && __range = range-init;
    for ( auto __begin = begin-expr,
                __end = end-expr;
            __begin != __end;
            ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

(そのすべてのガビンの意味についてのさらなる説明が続きます__)。

もちろん、この標準は、 insideステートメントの代わりに直接使用する場合と比較して、その行がパフォーマンスのオーバーヘッドをもたらすかどうかを保証するものではありません。実装では、ポインタをスタックスロットに手間をかけてコピーし、参照が使用されるたびにそれを読み戻すことで参照を実装できます。最適化する必要はありません。const auto &e = *__begin*__begine

__beginしかし、がコンテナイテレータ(operator*参照を返す)であり、ステートメントeで値によって渡される場合、賢明なコンパイラにオーバーヘッドが存在する必要がある理由はありません。

于 2012-10-24T21:16:40.287 に答える