7265


Range-based for loopで参照にした時としないときではどのような違いがあるでしょうか。  
特に速度的な面では違いがあるのでしょうか？例えばvectorをRange-based for
loopで回す場合、単に値を書き換えることなく使う場合、通常のケースでは下記のどのケースが良いのでしょうか？直感的には3だと思われるのですが、今まで常に1を使っていました。

[code]

    vector<string> vec =
    {
        "foo", "bar", "baz"
    };
    // case 1
    for ( auto it : vec )
    {
        cout << it << endl;
    }
    // case 2
    for ( auto &it : vec )
    {
        cout << it << endl;
    }
    // case 3
    for ( const auto &it : vec )
    {
        cout << it << endl;
    }
    
[/code]

追記 -------------  
皆さま、申し訳ございません。確かに質問として成り立っておりませんでした。  
私のほうでCygwin上のgcc 4.8.3でのベンチマークを追記いたします。  
100万行程度では差がほとんど見られなかったので  
ランダムなアルファベット文字列1000万行のテキストで最適化オプションなしで  
計測いたしました。

ケース1 参照なしのパターン

[code]

    #include <iostream>
    #include <fstream>
    #include <chrono>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        vector<string> v;
    
        ifstream ifs("test_mil.txt");
        if (ifs.fail()) return -1;
    
        for (string line; getline(ifs, line); )
        {
            v.push_back(line);
        }
    
        const auto startTime = chrono::system_clock::now();
    
        string ss;
        int i = 0;
        for (auto s : v)
        {
            if (i % 2 == 0)
                ss = s.substr(i % s.size());    
            else
                ss = s;
            cout << (i % 2);
            i++;
        }
        cout << '\n';
    
        const auto endTime   = chrono::system_clock::now();
        const auto timeSpan = endTime - startTime;
        cout << "処理時間: "
         << chrono::duration_cast<chrono::milliseconds>(timeSpan).count() 
         << " [ms]" << std::endl;
    
        cout << ss << endl;
    
        return 0;
    }
    
[/code]

ケース2 参照のパターン

[code]

    #include <iostream>
    #include <fstream>
    #include <chrono>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        vector<string> v;
    
        ifstream ifs("test_mil.txt");
        if (ifs.fail()) return -1;
    
        for (string line; getline(ifs, line); )
        {
            v.push_back(line);
        }
    
        const auto startTime = chrono::system_clock::now();
    
        string ss;
        int i = 0;
        for (auto &s : v)
        {
            if (i % 2 == 0)
                ss = s.substr(i % s.size());    
            else
                ss = s;
            cout << (i % 2);
            i++;
        }
        cout << '\n';
    
        const auto endTime   = chrono::system_clock::now();
        const auto timeSpan = endTime - startTime;
        cout << "処理時間: "
         << chrono::duration_cast<chrono::milliseconds>(timeSpan).count() 
         << " [ms]" << std::endl;
    
        cout << ss << endl;
    
        return 0;
    }
    
[/code]

計測結果：  
ケース1 3123ms 3125ms 3135ms 平均 3128ms  
ケース2 2944ms 2927ms 2935ms 平均 2935ms  
ということでケース2のほうが速いことがわかりました。  
また次のようなコードを書くと

[code]

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class mystring : public std::string {
    public:
    
        mystring(const char *str) : std::string(str) { }
    
        // コピーコンストラクタ
        mystring(const mystring &rhs) : string(rhs.c_str())
        {
            cout << "mystring copy constructor" << endl;
        }
    
    };
    
    int main()
    {
        vector<mystring> v;
        mystring m("abc");
        v.push_back(m);
    
        cout << "test start" << endl;
        for (auto &s : v)
        {
            cout << s << endl;
        }
        cout << "test end" << endl;
    
        return 0;
    }
    
[/code]

このケースではコピーコンストラクタが呼び出されず、  
for (auto s : v)としたほうではコピーコンストラクタが呼び出されました。  
よって私の環境下につきましてはjoy1192さんの解答の通りとなりました。


CC BY-SA 3.0
2015-03-01T05:00:39.270
7263
2015-03-02T06:53:53.643
2015-03-01T17:05:04.567
7744
7744
question
c++
Range-based for loopで参照にした時としない時の速度面での違い


case 2,case 3は用途によって使い分けるべきですが、一般的な使い方の上ではcase 1は遅くなります。

case 1の場合、  
`vector<T>`の要素を1つ1つ`auto
it`にコピーしていきます。つまり、毎回コピーコンストラクタが呼び出されるため、単純なアクセス用途であれば無駄なコストが発生します。  
`for`ループ内でコピーした値に対して破壊的な操作を行いたい場合に使用するケースは有るでしょう。

case 2, case 3の場合、  
`vector<T>`内の要素に対する参照を渡しているだけなので、高速です。  
原理的にはポインタのコピー程度のコストしか発生しません。constの有無は速度には大きく関係しないはずです。


CC BY-SA 3.0
2015-03-01T06:03:07.903
7265
2015-03-01T06:03:07.903
6119
7263
answer


質問文の要件にある通り、コンテナ型`vector<string>` かつ 要素書き換え無し ならば、 case 3:`cosnt auto&`
一択でよいでしょう。

一般的に case 3:`const auto&` と case 2:`auto&`
とで速度的な差異は生じませんが、要素の書き換えを伴わないforループならば、コーディング誤りを防ぐためにもconst修飾指定が望ましいです。case
1:`auto`
は、コンテナの要素型が基本型でもない限り避けるべきです。無駄な要素コピーによって速度的なペナルティを受けますし、要素型がコピー不可な場合はコンパイルエラーを引き起こします。

一般論としては、目的に応じ下記2種類を使い分けるのがベターです。

  * `for (auto&& e : c)`：要素`e`を書き換える可能性がある
  * `for (const auto& e : c)`：要素`e`を参照するだけ

関連情報として、次期C++標準（C++14の次）に向けて提案さていた、Terse Range-Based
forの[初期提案文書](http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2014/n3853.htm)にも次の言及があります。ただし、この提案は最終的に[却下されました](http://ezoeryou.github.io/blog/article/2014-12-11-tarse-
range-based-for-removed-from-clang.html)。

> "for (auto elem : range)" is very tempting and very bad. It produces "auto
> elem = *__begin;" (see 6.5.4 [stmt.ranged]/1), which copies each element,
> which is bad because:
>
>   * It might not compile - [...]
>   * It might misbehave at runtime - [...]
>   * It might be inefficient - [...]
>

余談ですが：`for (～ it :
vec)`の変数名`it`はイテレータを連想させる名前なので、`s(tr)`とか`e(lem)`の方が良いかもしれませんね。


CC BY-SA 3.0
2015-03-02T01:48:33.623
7302
2015-03-02T06:53:53.643
2015-03-02T06:53:53.643
49
49
7263
answer