Подтвердить что ты не робот

Использование объявленной переменной в цикле for-loop

В приведенном ниже примере i имеет область действия. Но, похоже, я не могу использовать i во втором цикле. Почему for (i : v1) не работает, но работает for (int i : v1)?

#include<iostream>
#include<string>
#include<vector>

int main()
{
    std::vector<int> v1;
    int i;
    while(std::cin>>i)
    {
        v1.push_back(i);
    }

    for(i : v1) //for (int i:v1) works
        std::cout<<i<<"\t";
    cout<<std::endl;
    return 0;
}
4b9b3361

Ответ 1

Это синтаксическая проблема, когда цикл for на основе диапазона требует объявления именованной переменной, то есть требует спецификатора типа (cf, например, cppreference.com):

для (range_declaration: range_expression) loop_statement

range_declaration - объявление именованной переменной, тип которой является типом элемента последовательности, представленной формулой range_expression или ссылку на этот тип. Часто использует авто спецификатор для автоматического вычитания типа

Вообще-то я не знаю, почему ваш вопрос получил вниз; Я считаю ваше предположение вполне нормальным; просто синтаксис С++ решил определить его по-другому.

Ответ 2

Основанный на диапазоне for специально предназначен для замены циклов, подобных следующему (это несколько упрощенное решение, основанный на диапазоне for, особенно версия С++ 17, более общий, чем пример)

for (auto it = range.begin(), end = range.end(); it != end; ++it) {
   use(*it);
}

В большинстве случаев они не будут использовать значения в разных местах, но скорее будут использовать элемент в самом месте:

  • При мутировании элементов в последовательности значение действительно не помогает.
  • В большинстве случаев копирование значений дорого и сохранение ссылки более эффективно.
  • Есть даже случаи, когда объекты не могут быть скопированы для начала.

В результате дизайнеры из диапазона for решили, что ссылки должны быть обязательно поддержаны. В то же время было предназначено использовать достаточно упрощенное правило перезаписи для диапазона for. Правило, которое кодируется в стандарте, таково:

for (<range-decl>: <range>) { <body> }

эквивалентно

{
    auto&& range = <range>;        // keep the range alive!
    auto   it    = begin(range);   // actually, reality is bit more complicated
    auto   end   = end(range);     // actually, reality is a bit more complicated
    for (; it != end; ++it) {
        <range-decl> = *it;        // this is the rewrite causing your issue
        <body>
    }
}

В частности, подразумевается, что <range-decl> является объявлением, а не просто именованием переменной. Причиной этого требования является то, что обычно объект, используемый перед :, является ссылкой. Однако ссылки не могут быть восстановлены. Однако в каждой итерации цикла может использоваться новая ссылка.

В принципе правило перезаписи может работать с использованием назначений, если <range-decl> не является объявлением, а скорее значением lvalue. Это даст свою долю нечетного поведения:

  • Будет разница между for (T const& x: range) и T const& x = 0; for (x: range): первая работает, а последняя является ошибкой.
  • Если lvalue является ссылкой на объект, расположенный где-то (T& x = get_reference(); for (x: range) {...}), цикл автоматически присваивает все значения в диапазоне объекту, расположенному где-то. Обычно объекты находятся либо в стеке, либо в исходном диапазоне (когда переменная объявлена ​​как ссылка).

Было сочтено более разумным только разрешить инициализацию, чем поддерживать инициализацию или назначения в зависимости от того, как объявлена ​​переменная. Рассматривая историю изменений предложений (N2930 и предшественники) не дает обсуждения, но я смутно помню, что обсуждалась эта точка.

Ответ 3

Когда вы используете циклы на основе диапазона, вам нужно объявление после открытия круглых скобок, а не только переменной. Правильный синтаксис:

 for ( declaration : range ) statement;

Вы можете увидеть эту ссылку для получения дополнительной информации.

В вашем примере: при объявлении i перед циклом while вы можете использовать его во всей функции main, а область действия - это функция main, Вы можете использовать его в теле for. Когда вы используете переменную i в вашем диапазоне for, тогда вы ее не объявляете, потому что вы уже объявили ее выше, поэтому она даст вам ошибку и не соответствует синтаксису С++.

Но когда вы набираете int перед i в скобке for, тогда вы объявляете другую переменную с именем i, но только для вашего цикла for, а затем это нормально с С++ синтаксис.

Ответ 4

Обоснование, скорее всего, связано с тем, что эта переменная будет ссылаться на присваивание копий, что станет потенциальным источником большой неэффективности и на практике почти никогда не будет целью... если тип поддерживает копирование. Поэтому они, вероятно, полагали, что лучше запретить это.