Reputation: 39109
Some refactoring led to a piece of code that led me to this minimal testcase:
int main () {
if (int i=1) {
/* IF-BLOCK */
} else {
throw i;
}
}
This compiles fine. However, I always assumed that i
is only visible to IF-BLOCK
, but it seems that it's not. Is this a compiler bug?
Also, why does the following work?
int main () {
if (int i=1) {
} else if (int i=2) {
} else {
throw i;
}
}
Note the second if
"re-declares" i
. Another compiler bug?
Upvotes: 9
Views: 2339
Reputation: 39109
No, this is actually correct behavior.
6.4 Selection statements [stmt.select]
A name introduced by a declaration in a condition (either introduced by the
type-specifier-seq
or the declarator of the condition) is in scope from its point of declaration until the end of the substatements controlled by the condition. If the name is re-declared in the outermost block of a substatement controlled by the condition, the declaration that re-declares the name is ill-formed. [ Example:if (int x = f()) { int x; // ill-formed, redeclaration of x } else { int x; // ill-formed, redeclaration of x }
— end example ]
(Emphasis mine)
This basically means that the scope of i
begins in the condition, and ends after the if
-block, where the else
-block is part of the if
-block, too.
Your second problem with the nested if
is based on the (wrong) assumption that an else-if
is part of the introductory if
, but it really isn't. The if (int i=2)
is the body of the first else
!
if (int i=1)
|
/ \
/ \
/ \
/ \
/ \
if-block else
|
if(int i=2)
/ \
/ \
/ \
if-block throw i
What this means in turn:
int main () {
if (int i=1) {
} else if (1) {
throw (i+2);
} else {
throw i;
}
}
This code is valid, as the i
-declaration is visible in throw (i+2);
, but it is still valid to hide the first i
, because in nested scopes, names can be overriden:
int main () {
if (int i=1) {
} else if (int i=2) {
throw (i+2); // now refers to `int i=2`
} else {
throw i;
}
}
So all in all, don't panic: Writing tokenizers or parsers or something using the pattern found in the last statement still works, the relevant new knowledge here is that any declaration in a condition spans the whole if
-tree, but can be overriden in any nested if
.
Also, be assured that the following is still invalid (even though it was valid in old compilers):
if (int i=0) {}
std::cout << i; // nope, not valid
Upvotes: 15