Диапазон для цикла с существующей переменной

Используя диапазон, основанный на цикле в С++ 11 с существующей переменной, я ожидал бы, что переменная будет заполнена значением последней итерации после цикла. Тем не менее, я получил разные результаты, когда тестировал его.

Пример:

#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> v;
  v.push_back(2);
  v.push_back(43);
  v.push_back(99);

  int last = -50;
  for (last : v)
    std::cout << ":" << last << "\n";

  std::cout << last;
  return 0;
}
  • MSVC 2013, похоже, не поддерживает диапазон, основанный на циклах без объявления типа
  • GCC-5.1 либо автоматически вводит новую переменную, либо возвращает ее к исходному значению, предоставляя

    : 2
    : 43
    : 99
    -50

Я думаю, что MSVC снова является MSVC, но как насчет GCC здесь? Почему last не 99 в последней строке?


Учитывая определение по стандарту, я ожидал бы поведения, описанного в первом предложении.

{
  auto && __range = range_expression ; 
  for (auto __begin = begin_expr, __end = end_expr; 
       __begin != __end; ++__begin) { 
    range_declaration = *__begin; 
    loop_statement 
  } 
} 

range_declaration last, а не int last, это должно изменить существующую переменную.

Ответы

Ответ 1

GCC реализовало стандартное предложение n3994, что предполагает, что for (elem : range) будет синтаксическим сахаром для for (auto&& elem : range). Это не превратилось в С++ 17, поэтому функциональность была удалена из более поздних версий GCC.

Именованная переменная, используемая для итерации по диапазону, должна быть объявлением в соответствии с [stmt.ranged], поэтому ваш код не должен компилироваться.

Ответ 2

Ваш код не компилируется, начиная с gcc 6.1 (и для всех версий clang):

main.cpp:12:8: error: range-based for loop requires type for loop variable
  for (last : v)
       ^
       auto &&

он выглядит как предыдущие версии, используемые здесь неявно. Тот факт, что вы получаете -50 как последний вывод, состоит в том, что for вводит локальную область для последнего, поэтому после окончания for используется последний из внешней области.


Я немного копал, и это было специально под gcc: N3994, terse range-for, который в ближайшее время делает следующее:

A range-based for statement of the form
    for ( for-range-identifier : for-range-initializer ) statement
is equivalent to
    for ( auto&& for-range-identifier : for-range-initializer ) statement

то он не сделал это до С++ 17 и был удален здесь:

https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=229632

Ответ 3

в соответствии со стандартом, диапазон, основанный на цикле, производит тот же результат, что и:

{
  auto&& __range = expression;
  for (auto __begin = begin-expression,
            __end = end-expression;
       __begin != __end;
       ++__begin)
  {
    declaration = *__begin;
    statement
  }
}

Как вы можете видеть, переменная итерации __begin определяется в своей области.

Почему последнее не 99 в последней строке?

У вас есть две переменные, называемые last. Один из них входит в объем вашей основной функции и имеет значение -50. Вторая переменная определяется в области диапазона, основанного на цикле.

Внутри цикла печать переменной last печатает одну из той же области (то есть из диапазона, основанного на цикле). После цикла, однако, печать last снова напечатает переменную из той же области действия, которая удерживает -50

Ответ 4

Ваша программа не скомпилируется с моим g++ 4.9.2.

Скомпилировать с помощью clang++ 3.5 (с предупреждением: "для цикла с неявным выведенным типом является расширение С++ 1z [-WС++ 1z-extensions]" )

Но clang++ использует разные переменные last

Со следующей измененной программой

#include <iostream>
#include <vector>
using namespace std;

int main() {
  std::vector<int> v;
  v.push_back(2);
  v.push_back(43);
  v.push_back(99);

  int last = -50;

  std::cout << "extern last pointer: " << long(&last) << '\n';

  for ( last : v)
   {
     std::cout << ": " << last << " ; pointer: " << long(&last) << '\n';
   }

  std::cout << "extern last pointer again: " << long(&last) << '\n';
  std::cout << ": " << last << std::endl;

  return 0;
}

Я получаю следующий вывод

extern last pointer: 140721376927168
: 2 ; pointer: 38101008
: 43 ; pointer: 38101012
: 99 ; pointer: 38101016
extern last pointer again: 140721376927168
: -50