16

Is it legal to declare a loop variable in a ranged-based for loop with the same name I use in the expression statement of the loop? I hope the example makes it clear.

#include <iostream>
#include <vector>

struct bar {
    std::vector<int> nums;
};

int main()
{
    bar b;
    b.nums = {1, 2, 3};

    for(int b : b.nums)
        std::cout << b << std::endl;   
}

gcc 4.8 gives an error while clang 3.2 allows it.

2
  • 5
    The bug has been reported to gcc. Commented May 6, 2013 at 21:20
  • @JesseGood Thanks for digging that up. Turns out I didn't find anything on bugzilla because I was searching for "ranged based for" instead of "range based for". Don't know how that term manifested in my head, same mistake happened in the title, too. Commented May 6, 2013 at 21:26

3 Answers 3

11

From my reading of C++2011 6.5.4, your code of:

bar b;

for(int b : b.nums)
    std::cout << b << std::endl;

Should be converted to:

bar b;

{
   auto && __range = b.nums;
   for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin ) {
       int b = *__begin;
       std::cout << b << std::endl;
   }
}

This to me means that clang is correct.

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

6 Comments

IMHO, I'd say that this is a defect in the standard. The visibility of int b should be from the : until the end of the for statement.
@rodrigo: I agreed until I thought about it some more. What would this mean: for (auto & x : x)?
That would be as meaningless as the simple declaration auto &x = x; and for the same reasons.
@rodrigo: Why? Why make that meaningless when what we have now currently works and makes sense?
@GManNickG: Why? For consistence with other parts of the language: everywhere when a name is introduced its scope starts just after the name itself. For example void *p = &p; is perfectly legal. The range-based-for is an exception to the rule and for no real reason other than a likely unwanted side-effect of the standard wording.
|
9

Clang is right.

Paragraph 6.5.4/1 of the C++11 Standard defines the range-based for statement as follows:

For a range-based for statement of the form

for ( for-range-declaration : expression ) statement

let range-init be equivalent to the expression surrounded by parentheses

( expression )

and for a range-based for statement of the form

for ( for-range-declaration : braced-init-list ) statement

let range-init be equivalent to the braced-init-list. In each case, a range-based for statement is equivalent to

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

From the above, it is visible that variable b, which corresponds to the for-range-declaration, is declared inside a nested block statement, while the initializer range-init (which corresponds to b.nums) appears in the parent scope, where b should resolve to the object of type bar.

4 Comments

@sftrabbit: You're right, I overlooked that initially. I edited the answer, thank you
It is not so easy... from a literal interpretation of the standard, in auto && __range = range-init; the range-init is actually b.num, but the int b does not enter in the definition until 5 lines later... But again, seeing the original code, it would be expected that the int b hides the bar b.
Apologies for leading you astray. My mistake! I agree with this answer.
@sftrabbit: That's all right, your observation was OK and I should have triple-checked rather than rushing into answering ;) Lesson learnt
2

For what it's worth, this bug has now been fixed on gcc trunk. :)

Comments

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.