Niklas R
Niklas R

Reputation: 16870

range based for loop with existing variable

Using a range based for loop in C++11 with an existing variable, I would expect that variable to be filled with the value of the last iteration after the loop. However, I've gotten different results when I tested it.

Example:

#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;
}
  1. MSVC 2013 doesn't seem to support range based for loops without type declaration
  2. GCC-5.1 either automatically introduces a new variable or sets it back to the initial value, giving

    :2
    :43
    :99
    -50

I guess MSVC is just being MSVC again, but what about GCC here? Why is last not 99 in the last line?


Given the definition by the standard, I would expect the behaviour I described in the first sentence.

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

range_declaration being last and not int last, this should modify the existing variable.

Upvotes: 28

Views: 4807

Answers (4)

marcinj
marcinj

Reputation: 49986

Your code does not compile starting with gcc 6.1 (and for all clang versions):

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

it looks like previous versions used auto implicitly here. The fact that you get -50 as last output is because for introduces local scope for last, so after for ends, last from outer scope was used.


I did a little digging and this was on purpose under gcc: N3994, terse range-for, which shortly is doing following:

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

then it didn`t make it to c++17 and was removed here:

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

Upvotes: 10

max66
max66

Reputation: 66200

Your program doesn't compile with my g++ 4.9.2.

Compile with clang++ 3.5 (with a warning: "range-based for loop with implicit deduced type is a C++1z extension [-Wc++1z-extensions]")

But clang++ use different last variables

With the following modified program

#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;
}

I get the following output

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

Upvotes: 2

TartanLlama
TartanLlama

Reputation: 65620

GCC implemented standards proposal n3994, which suggests that for (elem : range) be syntactic sugar for for (auto&& elem : range). This didn't make it into C++17, so the functionality has been removed from more recent versions of GCC.

The named variable used to iterate over the range must be a declaration according to [stmt.ranged], so your code shouldn't compile.

Upvotes: 23

Patrik H
Patrik H

Reputation: 459

according to the standard, a range based for loop is producing the same output as:

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

As you can see, the iteration variable __begin is being defined in its own scope.

Why is last not 99 in the last line?

You have two variables called last. One is in the scope of your main function and holds the value -50. The second variable is defined in the scope of the range based for loop.

Inside the loop, printing the last variable prints the one from the same scope (ie. from the range based for loop). After the loop however, printing last will again print the variable from the same scope which is the one holding -50

Upvotes: 0

Related Questions