2012-10-24 3 views
32

私自身は最近C++ 11、より多くのを使用して、どこで私は過去にイテレータを使用されていたであろう見つけ、私は今range-based for loops可能な限りを使用しています:基本型を反復処理するときにconst参照を使用することの欠点はありますか?

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の範囲ベースを使用して、私は同じ動作を得るために、const参照のループ変数を使用する必要があります。

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

そして、これは導入しないならば、突然私は、もはや確認されませんでしたautoがシンプルな型の場合(参照を実装するシーンの後ろのポインタなど)、不要なアセンブリ命令。

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

質問:この規格には何らかの保証はありますか?

(私は問題が本当にループのためのベースのレンジに関連していないことを実現しています。const_iteratorsを使用するときにも存在だ。)

答えて

12

標準コンテナしかし彼らのイテレータ(ノート、からのすべての戻りの参照を、いくつかのこと」コンテナは実際にはコンテナではありません。例えば、std::vector<bool>はプロキシを返します)他のイテレータは、厳密にはサポートされていませんが、プロキシや値を返すことがあります。どんな種類のパフォーマンス関連機能(複雑性の保証を超えて)も実装の品質とみなされます。

012それは前に行ったようにあなたがコンパイラを持っ検討する必要があります、と述べた

はあなたのための選択を行います。

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

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

+1

または単に... 'auto const &'. ;) – Xeo

+1

@Xeo:イテレータが値に対する[const]参照ではなく値を生成する場合、表記法「T &&」はその値を値。 –

+0

*イテレータの利便性に関わらず、Huh * * auto &&は常に参照になります。それが実際に値を生成する場合、それはちょうどrvalue参照になります。 (注:私はあなたのコメントを誤解しているかもしれません。) – Xeo

11

6.5.4/1言う:

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

LET範囲-INITブレース-のinit-リストに相当します。各場合において、 範囲ベースのfor文は、(さらなる説明は、すべてのその__ gubbinsの意味の以下)

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

と等価です。

標準は、その行const auto &e = *__beginが直接声明*__begin代わりのeを使用した場合に比べて、当然のことながら、パフォーマンスのオーバーヘッドを紹介するかどうかを任意の保証がありません。インプリメンテーションでは、ポインタをいくつかのスタックスロットに面倒にコピーし、リファレンスが使用されるたびに読み戻し、最適化する必要はありません。

しかし、賢明なコンパイラでのオーバーヘッドがあるはずない理由は__beginは(operator*参照を返します)コンテナイテレータで、その後、e声明に値渡しされる場合には、ありません。

+0

Steveに感謝します。だから、どのように一般的な範囲ベースのforループを書くだろうか? –

+0

@ダニエル:私は 'for(const auto&e:coll){foo_func(e); } 'は問題ありませんが、私はC++ 11を使用していないので、それを指示するスタイルガイドを書くことができると主張しています。 'boost :: call_traits :: param_type'の使い方は、一度パラメータを渡すと、小さなオブジェクト型ではなく参照を使うことに潜在的なオーバーヘッドがあります。関数内では、コンパイラが参照変数を「純粋な」エイリアスとして扱うことを信頼していると思います。 '* __ begin'が値で返ってきたら、余分な一時的なものをチェックせずに回避することにはなりません。 –

関連する問題