Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why special rules for `for` statement scope?

Tags:

c++

I stumbled recently in this problem

for(int i=0,n=v.size(); i<n; i++) {    ...    P2d n = ...   <<<--- error here } 

the compiler was complaining about the fact that the n local variable has been already defined, despite that the open brace looks like it should start a new scope.

Indeed the standard has a special wording for this and while the code compiled fine with g++4.6.3, it complains with more recent versions and other compilers.

What is the rationale (if there is any) behind this special rule?

To be more clear: the standard explains that this is not permitted and I've no questions about the technical reason for which that's an error: I was just wondering why the committee decided to use special extra rules instead of just creating another nested scope when seeing the opening brace (like it happens in other places).

For example to make the code legal you can just wrap the body with two brace pairs instead of one...

Please also note that braces after for/while/if, while considered good practice, are not mandatory and not part of the syntax, but still a scope containing the loop variables exists (therefore using function definition as another example where the scope of the locals is the body of the function is not relevant: a function body is not a statement and braces are mandatory).

In the C++ syntax the body of a for is just a statement; however if this statement happens to be a braced group then it gets a special handling in for/while/if (that doesn't happen when you use a braced group as statement elsewhere in the language).

What is the reason for adding this extra complication to the language? It's apparently not needed and just treating the braces as another inner scope seems (to me) simpler.

Are there use cases in which this simpler and more regular approach doesn't work?

Note that I'm not asking opinions. Either you know why the committee took this decision (requiring also a quite elaborate wording in the standard instead of just having the body as a regular statement with the regular handling of a brace enclosed block when used as statement) or you don't.

EDIT

The "single scope" view for the syntax is for me unnatural but technically possible for the for statement that can be rationalized as a single block with a backward goto statement, but it's hard to defend in a very similar case for the if statement:

if (int x = whatever()) {     int x = 3; // Illegal } else {     int x = 4; // Illegal here too } 

but this is instead legal

if (int x = whatever()) {     int z = foo(); } else {     int z = bar(); } 

So are the condition, the then part and the else part of an if statement the same scope? No because you can declare two z variables. Are they separate scopes? No because you cannot declare x.

The only rationalization I can see is that the then and else part are indeed separate scopes, but with the added (strange) rule that the variable declared in the condition cannot be declared in the scope. Why this extra strange limitation rule is present is what I'm asking about.

like image 782
6502 Avatar asked Oct 15 '14 08:10

6502


People also ask

What are the scope rules?

The scope rules answer these questions. In fact, scope rules tell us if an entity (i.e., variable, parameter and function) is "visible" or accessible at certain places. Thus, places where an entity can be accessed or visible is referred to the scope of that entity. in which it is declared.

How does a compiler implement scope rules?

Whenever a scope is introduced, the compiler gives it a name and puts it in a structure (a tree) that makes it easy to determine the position of that scope in relation to other scopes, and it is marked as being the current scope. When a variable is declared, its assigned to the current scope.

Can you tell us something about scope rules in C ++?

When you declare a program element such as a class, function, or variable, its name can only be "seen" and used in certain parts of your program. The context in which a name is visible is called its scope. For example, if you declare a variable x within a function, x is only visible within that function body.

What is the scope of function in C?

A scope in any programming is a region of the program where a defined variable can have its existence and beyond that variable can not be accessed. There are three places where variables can be declared in C programming language: 1. Inside a function or a block which is called local variables, 2.


2 Answers

int i = 0; for (MyObject o1; i<10; i++) {    MyObject o2; } 

Can be translated from the point view of recent compilers into:

int i = 0; {     MyObject o1;     Label0:     MyObject o2; //o2 will be destroyed and reconstructed 10 times, while being with the same scope as o1     i++;     if (i < 10)         goto Label0; } 

This is the answer to your last question mark at the end, they didn't add something complicated, just used goto to label in the same scope, and not goto to out of the scope and then enter to it again. I don't see clear reason why it's better. (While it will do some incompatibility with older codes)

like image 60
KugBuBu Avatar answered Oct 08 '22 14:10

KugBuBu


The semantics are not special for the for loop! if (bool b = foo()) { } works the same. The odd one out is really a { } block on its own. That would be rather useless if it didn't introduce a new scope. So the apparent inconsistency is due to a misplaced generalization from an exceptional case.

[edit] An alternative view would be to consider an hypothetical, optional keyword:

// Not a _conditional_ statement theoretically, but grammatically identical always() {     Foo(); } 

This unifies the rules, and you wouldn't expect three scope (inside, intermediate,outside) here either.

[edit 2] (please don't make this a moving target to answer)

You wonder about lifetime and scopes (two different things) in

int i = 0; for (MyObject o1; i<10; i++) {    MyObject o2; } 

Let's generalize that:

MyObject o2; // Outer scope int i = 0; for (MyObject o1; i<o1.fooCount(); i++) {    std::cout << o2.asString();    MyObject o2; } 

Clearly the call to o2.asString() refers to the outer o2, in all iterations. It's not like the inner o2 survives the loop iteration. Name lookup doesn't will use names from the outer scope when the names aren't yet defined in the inner scope - and "not yet defined" is a compile-time thing. The repeated construction and destruction of the inner o2 is a runtime thing.

like image 38
MSalters Avatar answered Oct 08 '22 15:10

MSalters