Использование объявленной переменной в цикле 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;
}
Ответы
Ответ 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
Обоснование, скорее всего, связано с тем, что эта переменная будет ссылаться на присваивание копий, что станет потенциальным источником большой неэффективности и на практике почти никогда не будет целью... если тип поддерживает копирование.
Поэтому они, вероятно, полагали, что лучше запретить это.