Каков правильный способ использования С++ 11 на основе диапазона for?
Какой синтаксис следует использовать? for (auto elem : container)
или for (auto& elem : container) или for (const auto& elem : container)?
Или какой-то другой?
Каков правильный способ использования С++ 11 на основе диапазона for?
Какой синтаксис следует использовать? for (auto elem : container)
или for (auto& elem : container) или for (const auto& elem : container)?
Или какой-то другой?
Пусть начнется дифференциация между наблюдением элементов в континенте против их изменения на месте.
Рассмотрим простой пример:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
    cout << x << ' ';
Вышеприведенный код печатает элементы (int s) в vector:
1 3 5 7 9
Теперь рассмотрим другой случай, когда векторные элементы являются не просто простыми целыми числами, но экземпляры более сложного класса, с настраиваемым конструктором копирования и т.д.
// A sample test class, with custom copy semantics.
class X
{
public:
    X() 
        : m_data(0) 
    {}
    X(int data)
        : m_data(data)
    {}
    ~X() 
    {}
    X(const X& other) 
        : m_data(other.m_data)
    { cout << "X copy ctor.\n"; }
    X& operator=(const X& other)
    {
        m_data = other.m_data;       
        cout << "X copy assign.\n";
        return *this;
    }
    int Get() const
    {
        return m_data;
    }
private:
    int m_data;
};
ostream& operator<<(ostream& os, const X& x)
{
    os << x.Get();
    return os;
}
Если мы используем приведенный выше синтаксис for (auto x : v) {...} с этим новым классом:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
    cout << x << ' ';
}
вывод выглядит примерно так:
[... copy constructor calls for vector<X> initialization ...] Elements: X copy ctor. 1 X copy ctor. 3 X copy ctor. 5 X copy ctor. 7 X copy ctor. 9
Как он может быть прочитан на выходе, вызовы  copy constructor выполняются во время
диапазон для итераций цикла. 
Это происходит потому, что мы захватываем элементы из контейнера  по значению
(часть auto x в for (auto x : v)).
Это неэффективный код, например. если эти элементы являются экземплярами std::string,
выделение памяти кучи может быть выполнено с дорогими поездками в диспетчер памяти и т.д.
Это бесполезно, если мы просто хотим наблюдать за элементами в контейнере.
Таким образом, доступен лучший синтаксис:  const ссылка, т.е.  const auto&:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{ 
    cout << x << ' ';
}
Теперь вывод:
[... copy constructor calls for vector<X> initialization ...] Elements: 1 3 5 7 9
Без ложного (и потенциально дорогого) вызова конструктора копии.
Итак, когда  наблюдает элементы в контейнере (то есть для доступа только для чтения),
следующий синтаксис подходит для простых дешевых типов, таких как int, double и т.д.:
for (auto elem : container) 
В общем случае запись в const лучше в общем случае,
чтобы избежать бесполезных (и потенциально дорогостоящих) вызовов конструктора копирования:
for (const auto& elem : container) 
Если мы хотим изменить элементы в контейнере, используя for на основе диапазона,
выше for (auto elem : container) и for (const auto& elem : container)
Синтаксис неверен.
Фактически, в первом случае elem хранит копию оригинала
элемент, поэтому внесенные в него изменения просто теряются и не сохраняются постоянно
в контейнере, например:
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)  // <-- capture by value (copy)
    x *= 10;      // <-- a local temporary copy ("x") is modified,
                  //     *not* the original vector element.
for (auto x : v)
    cout << x << ' ';
Результат - это только начальная последовательность:
1 3 5 7 9
Вместо этого попытка использования for (const auto& x : v) просто не скомпилируется.
g++ выводит сообщение об ошибке примерно так:
TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x' x *= 10; ^
Правильный подход в этом случае заключается в захвате с помощью ссылки const:
vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
    x *= 10;
for (auto x : v)
    cout << x << ' ';
Выход (как ожидалось):
10 30 50 70 90
Этот синтаксис for (auto& elem : container) также работает для более сложных типов,
например учитывая vector<string>:
vector<string> v = {"Bob", "Jeff", "Connie"};
// Modify elements in place: use "auto &"
for (auto& x : v)
    x = "Hi " + x + "!";
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
    cout << x << ' ';
вывод:
Hi Bob! Hi Jeff! Hi Connie!
Предположим, что a vector<bool>, и мы хотим инвертировать логическое логическое состояние
его элементов, используя следующий синтаксис:
vector<bool> v = {true, false, false, true};
for (auto& x : v)
    x = !x;
Вышеприведенный код не компилируется.
g++ выводит сообщение об ошибке, подобное этому:
TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen ce {aka std::_Bit_reference}' for (auto& x : v) ^
Проблема в том, что шаблон std::vector специализирован для bool, с
реализация, которая упаковывает bool для оптимизации пространства (каждое логическое значение
хранится в одном бите, восемь "булевых" битов в байте).
Из-за этого (поскольку невозможно вернуть ссылку на один бит)
vector<bool> использует так называемый шаблон  "прокси-итератор" .
"Инитор-прокси" - это итератор, который при разыменовании не дает
обычный bool &, но вместо этого возвращает (по значению) временный объект,
который является класс прокси конвертируемых в bool.
(См. Также Этот вопрос и связанные ответы здесь, на StackOverflow.)
Чтобы изменить на месте элементы vector<bool>, новый вид синтаксиса (используя auto&&)
необходимо использовать:
for (auto&& x : v)
    x = !x;
Следующий код работает нормально:
vector<bool> v = {true, false, false, true};
// Invert boolean status
for (auto&& x : v)  // <-- note use of "auto&&" for proxy iterators
    x = !x;
// Print new element values
cout << boolalpha;        
for (const auto& x : v)
    cout << x << ' ';
и выходы:
false true true false
Обратите внимание, что синтаксис  for (auto&& elem : container) также работает в других случаях
обычных (не-прокси) итераторов (например, для vector<int> или vector<string>).
(Как примечание, вышеупомянутый синтаксис "наблюдения" for (const auto& elem : container) отлично работает и для итератора прокси-сервера.)
Вышеприведенное обсуждение можно резюмировать в следующих руководствах:
Для элементов наблюдения используйте следующий синтаксис:
for (const auto& elem : container)    // capture by const reference
Если объекты дешевы для копирования (например, int s, double s и т.д.),
можно использовать слегка упрощенную форму:
for (auto elem : container)    // capture by value
Для изменения элементов на месте используйте:
for (auto& elem : container)    // capture by (non-const) reference
Если в контейнере используются "итераторы прокси" (например, std::vector<bool>), используйте:
for (auto&& elem : container)    // capture by &&
Конечно, если есть необходимость сделать  локальную копию элемента внутри тела цикла, то захват по значению (for (auto elem : container)) является хорошим выбором.
В общем коде, поскольку мы не можем делать предположения о том, что общий тип T дешев для копирования, в режиме  наблюдения безопасно всегда использовать  for (const auto& elem : container). 
(Это не вызовет потенциально дорогостоящие бесполезные копии, будет отлично работать и для дешевых типов копий типа int, а также для контейнеров с использованием прокси-итераторов, таких как std::vector<bool>.)
Кроме того, в режиме  изменения, если мы хотим, чтобы общий код работал и в случае прокси-итераторов, лучшим вариантом является  for (auto&& elem : container). 
(Это будет отлично работать также и для контейнеров с использованием обычных не-прокси-итераторов, таких как std::vector<int> или std::vector<string>.)
Таким образом, в общем коде могут быть предоставлены следующие рекомендации:
Для наблюдения элементов используйте:
for (const auto& elem : container)
Для изменения элементов на месте используйте:
for (auto&& elem : container)
Нет правильного способа использования for (auto elem : container), или for (auto& elem : container) или for (const auto& elem : container). Вы просто выражаете то, что хотите.
Позвольте мне подробнее остановиться на этом. Позвольте прогуляться.
for (auto elem : container) ...
Это синтаксический сахар для:
for(auto it = container.begin(); it != container.end(); ++it) {
    // Observe that this is a copy by value.
    auto elem = *it;
}
Вы можете использовать этот, если в вашем контейнере содержатся элементы, которые дешево копировать.
for (auto& elem : container) ...
Это синтаксический сахар для:
for(auto it = container.begin(); it != container.end(); ++it) {
    // Now you're directly modifying the elements
    // because elem is an lvalue reference
    auto& elem = *it;
}
Используйте это, если вы хотите, например, писать элементы в контейнере.
for (const auto& elem : container) ...
Это синтаксический сахар для:
for(auto it = container.begin(); it != container.end(); ++it) {
    // You just want to read stuff, no modification
    const auto& elem = *it;
}
Как говорится в комментарии, просто для чтения. И что об этом, все правильно "правильно" при правильном использовании.
Правильное средство всегда
for(auto&& elem : container)
Это гарантирует сохранение всей семантики.
В то время как первоначальная мотивация цикла range-for могла быть легкостью итерации по элементам контейнера, синтаксис достаточно общий, чтобы быть полезным даже для объектов, которые не являются чисто контейнерами.
Синтаксическое требование для цикла for заключается в том, что range_expression поддерживают begin() и end() как функции - либо как функции-члены того типа, который он оценивает, либо как функции, не являющиеся членами, что принимает экземпляр типа.
В качестве надуманного примера можно создать диапазон чисел и выполнить итерацию по диапазону, используя следующий класс.
struct Range
{
   struct Iterator
   {
      Iterator(int v, int s) : val(v), step(s) {}
      int operator*() const
      {
         return val;
      }
      Iterator& operator++()
      {
         val += step;
         return *this;
      }
      bool operator!=(Iterator const& rhs) const
      {
         return (this->val < rhs.val);
      }
      int val;
      int step;
   };
   Range(int l, int h, int s=1) : low(l), high(h), step(s) {}
   Iterator begin() const
   {
      return Iterator(low, step);
   }
   Iterator end() const
   {
      return Iterator(high, 1);
   }
   int low, high, step;
}; 
Со следующей функцией main,
#include <iostream>
int main()
{
   Range r1(1, 10);
   for ( auto item : r1 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
   Range r2(1, 20, 2);
   for ( auto item : r2 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
   Range r3(1, 20, 3);
   for ( auto item : r3 )
   {
      std::cout << item << " ";
   }
   std::cout << std::endl;
}
можно получить следующий вывод.
1 2 3 4 5 6 7 8 9 
1 3 5 7 9 11 13 15 17 19 
1 4 7 10 13 16 19