Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of variable declared in condition

Tags:

c++

scope

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?

like image 754
Sebastian Mach Avatar asked Aug 14 '13 07:08

Sebastian Mach


People also ask

What is the scope of a variable declaration?

Scope is the context in which a variable, procedure, class, or type is declared. Scope affects the accessibility of an item's value outside that context. For example, variables declared within a procedure are typically not available outside of the scope of that procedure.

Can we declare variable in if condition?

If you're new to the syntax that's used in the code sample, if (int i = 5) { is a perfectly valid way of declaring and defining a variable, then using it inside the given if statement. It allows us to write terser, clearer code, while also avoiding limiting the scope of a variable.

What is the scope of an if statement?

if is a conditional statement, which returns true or false based on the condition that is passed in it's expression. By default, if-statement is implemented on only one line, that follows it. But if we put block after the if-statement, it will be implemented on the whole block.


1 Answers

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
like image 90
Sebastian Mach Avatar answered Oct 12 '22 22:10

Sebastian Mach