28

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.

4
  • 4
    Does that even compile? I thought the last bit had to be a declaration. Commented Jul 27, 2016 at 13:43
  • 2
    Ah, this must be them pre-emptively implementing n3994 which didn't make it into C++17. Commented Jul 27, 2016 at 13:48
  • @TartanLlama Thanks, I see now that "range_declaration" was chosen for a good reason. I wasn't able to deduce that it actually had to be a declaration and thought it could be any assignable expression. Commented Jul 27, 2016 at 13:51
  • related answers: stackoverflow.com/q/32706051/86967 Commented Feb 26, 2018 at 5:24

4 Answers 4

23

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I'm accepting this answer as it is the only one stating that the left side of the : actually has to be a declaration, not an arbitrary expression, and contains a reference to the standard. Thanks! :)
10

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

5 Comments

So MSVC wasn't being MSVC again!
newest VS (webcompiler.cloudapp.net) also does not compile this.
gcc 5.1.0 compiles it in c++1z mode
5.2.0 and 5.3.0 compiles it either
@Borgleader OP did not say that it requires c++1z
2

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

Comments

0

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

2 Comments

declaration has to be an actual declaration. And last is not.
@Nicol, I'm answering Niklas' second question to why he's getting the values he's stating in GCC5.1. Changing last to an actual type declaration would not change that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.