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.
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.
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.
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.
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.
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.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With