Цикл на основе С++ 11: получить элемент по значению или ссылке на const

Читая некоторые примеры циклов, основанных на диапазонах, они предлагают два основных способа: 1, 2, 3, 4

std::vector<MyClass> vec;

for (auto &x : vec)
{
  // x is a reference to an item of vec
  // We can change vec items by changing x 
}

или

for (auto x : vec)
{
  // Value of x is copied from an item of vec
  // We can not change vec items by changing x
}

Ну.

Когда нам не нужно менять элементы vec, IMO, примеры предлагают использовать вторую версию (по значению). Почему они не предлагают то, что const ссылки (по крайней мере, я не нашел прямого предложения):

for (auto const &x : vec) // <-- see const keyword
{
  // x is a reference to an const item of vec
  // We can not change vec items by changing x 
}

Разве это не лучше? Не избегает ли избыточная копия на каждой итерации, пока она const?

Ответ 1

Если вы не хотите изменять элементы, а также хотите избежать копирования, тогда auto const & является правильным выбором:

for (auto const &x : vec)

Тот, кто предлагает вам использовать auto &, неверен. Игнорируйте их.

Вот краткое описание:

  • Выберите auto x, когда вы хотите работать с копиями.
  • Выберите auto &x, когда вы хотите работать с оригинальными элементами и можете их изменить.
  • Выберите auto const &x, когда вы хотите работать с оригинальными элементами и не будете изменять их.

Ответ 2

Если у вас есть std::vector<int> или std::vector<double>, тогда просто отлично использовать auto (со значением copy) вместо const auto&, так как копирование int или double дешево:

for (auto x : vec)
    ....

Но если у вас есть std::vector<MyClass>, где MyClass имеет некоторую нетривиальную семантику копирования (например, std::string, некоторый сложный пользовательский класс и т.д.), то я бы предложил использовать const auto& для избегать глубоких копий:

for (const auto & x : vec)
    ....

Ответ 3

Когда нам не нужно менять элементы vec, в примерах предлагается использовать первую версию.

Затем они дают неправильное предложение.

Почему они не предлагают то, что const ссылается

Потому что они дают неправильное предложение:-) То, что вы упоминаете, является правильным. Если вы хотите, чтобы наблюдал объект, нет необходимости создавать копию, и нет необходимости иметь ссылку на него const.

EDIT:

Я вижу ссылки, на которые вы ссылаетесь, приводят примеры итерации по диапазону значений int или некоторого другого фундаментального типа данных. В этом случае, поскольку копирование int не дорогое, создание копии в основном эквивалентно (если не более эффективному), имеющему наблюдение const &.

Это, однако, не в общем случае для пользовательских типов. UDT могут быть дорогими для копирования, и если у вас нет причин для создания копии (например, для изменения извлеченного объекта без изменения исходного), то предпочтительно использовать const &.

Ответ 4

Я собираюсь быть противоположным здесь и сказать, что нет необходимости в auto const & в диапазоне, основанном на цикле. Скажите, если вы считаете, что следующая функция глупа (не в своей цели, а в том, как она написана):

long long SafePop(std::vector<uint32_t>& v)
{
    auto const& cv = v;
    long long n = -1;
    if (!cv.empty())
    {
        n = cv.back();
        v.pop_back();
    }
    return n;
}

Здесь автор создал ссылку на const для v для использования для всех операций, которые не изменяют v. Это глупо, на мой взгляд, и тот же аргумент может быть использован для использования auto const & в качестве переменной в диапазоне, основанном на цикле, а не просто auto &.