Accessing variables in an enclosing scope(not global) hidden by local declaration in C++?

Viewed 295

The scope resolution operator :: can be used to access a global variable shadowed by a local declaration. How do you achieve the same for a variable declared in an enclosing scope but not global?

int main() {
  int i = 10;

  if (1) {
    int i = 5;
    std::cout << "Local i: " << i << std::endl;

    std::cout << "Main's i: " << ?? << std::endl; //How to access main's i?
  }

  return 0;
}

I know this is bad practice. But I was just curious to know if this was possible. I reckon it's not.

An explanation of why it's not possible or how it is would be useful.

3 Answers

This is unfortunately not possible. Compiler warning options, like -Wshadow for GCC, can help avoiding such situations:

-Wshadow

Warn whenever a local variable or type declaration shadows another variable, parameter, type, class member (in C++), or instance variable (in Objective-C) or whenever a built-in function is shadowed. Note that in C++, the compiler warns if a local variable shadows an explicit typedef, but not if it shadows a struct/class/enum. Same as -Wshadow=global.

In your example, for instance, you'd get a warning like:

: In function 'int main()':

:7:9: error: declaration of 'i' shadows a previous local [-Werror=shadow]

7 |     int i = 5;

  |

As @L. F. points out in a comment below, you can use references to still have access to the other i:

#include <iostream>

int main() {
  int i = 10;
  if (1) {
    int& nonlocal_i = i; // just before shadowing
    int i = 5;
    std::cout << "Local i: " << i << std::endl;
    std::cout << "Main's i: " << nonlocal_i  << std::endl;
  }
  return 0;
}

But -Wshadow will still complain, and if you were going the extra mile to find an alternative name, you could just name the local i differently.


Note: As user4581301 points out in a comment, code like int& i = i; doesn't do what you'd expect in an inner scope:

#include <iostream>

int main()
{
    int i = 4;
    {
        int& i = i;
        std::cout << i;
    }
}

It tries to use the variable i to initialize itself. In Microsoft's compiler you get a compilation error like:

error C4700: uninitialized local variable 'i' used

In GCC, if you turn all the warnings on, you get this message:

error: reference 'i' is initialized with itself [-Werror=init-self]

But it silently compiles and does the wrong thing if you don't have the warnings turned on

You can access the outer variable by creating a reference to it prior to shadowing it:

int main()
{
    int i = 10;
    if (1)
    {
        int &main_i = i;
        int i = 5;
        std::cout << "Local i: " << i << std::endl;
        std::cout << "Main's i: " << main_i << std::endl;
    }
    return 0;
}

No you can't use :: resolution operator for enclosing scopes. '::' operator can be used used as both unary and binary. You can use the unary scope operator if a namespace scope or global scope name is hidden by a particular declaration of an equivalent name during a block or class. For example -

#include <iostream>  
using namespace std;  

int tVar = 0;
int main(void) {
   int tVar = 0;
   ::tVar = 5;  // set global tVar to 5
   tVar = 6;    // set local tVar to 6
   cout << ::tVar << ", " << tVar;
   return 0;
}

and the binary use case of :: is what you've mentioned. Also to understand c++ blocks this article may help - http://www.ccplusplus.com/2011/08/scope-of-identifiers-in-c.html

Related