Table of Contents
What is a scope ?
A scope is where an identifier is declared , it can be thought of , as containing a list of identifiers . There are multiple scopes in C++
, hence multiple places , where identifiers can be declared .
An identifier can only be defined once in the same scope . An identifier in C++
, is just a a name which can be given , for example , for a namespace , for a class , for a function , for a variable … In different scopes , identifiers with the same name can be defined .
There are six scopes , in C++
. They are , the file scope also known as the global scope ; the namespace scope , the class scope , the block scope also known as the local scope ; the function scope , and the prototype scope . So in these scopes , identifiers can be declared . Scopes , can be nested , in one another .
name lookup , is the act of searching for an identifier in the different accessible scopes .
The file scope , or the global scope
The file scope , or the global scope , is also called the translation unit scope . A translation unit , is the resulting source code , after having performed preprocessor directives , such as #include
, or #define
.
An identifier declared in the translation unit scope , or file scope , is declared outside any other scope .
/* file : myMath.h */ int add(int a , int b ){ return a + b ;} float add(float a, float b){ return a + b ;} /* add , is an identifier declared in the file scope , for a function , which is overloaded .*/ /* file : myProg.cpp */ #include "myMath.h" /*Includes the file myMath.h . After preprocessing , a single translation unit , or source code is created . The global identifiers are : add , var_i , var1_i , var_f , var1_f , main .*/ int var_i = 0; int var1_i = 1; float var_f = 1.0f; float var1_f = 3.0f; int main(void ){ int var1_i = 3; int result_i = add(var_i , var1_i ); /*add var_i which is equal to 0 , with var1_i , which is equal to 3 , the result 3 , is stored , in result_i .*/ float var1_f = -1.0f; float result_f = add(var_f , ::var1_f ); /*add var_f which has a value of 1.0f , to the variable qualified by the global scope var1_f . ::var1_f has a value of 3.0f . The result is 4.0f , and is stored in var1_f .*/ }
An identifier declared in the global scope , is visible and can be accessed , starting the point after which it has been declared , from within the global scope , or from within any nested scope .
For example , var_i
in the previous example , is visible in the scope delimited by the curly braces after void )
, hence its value was used when calling the function add
.
As a rule , identifiers declared in parent scopes , are visible within nested scopes . In name lookup , the nested scope is searched first , and parent scopes are also searched . Name lookup starts from the scope where an identifier is used .
So in the previous example , the value of var1_i
is 3
, because var1_i
is used in the scope delimited by the curly brackets after void )
, where a call to the add
function is performed , hence name lookup starts in this scope .
It can be specified where the name lookup is to be performed , by using the scope resolution operator ::
. When an identifier is qualified by a scope , only the qualified by scope is searched for this identifier .
Hence in the previous example , ::var1_f
, is qualified with the global scope , the global scope is searched for the identifier var1_f . ::var1_f
has a value of 3.0f
.
The namespace scope
A namespace is used to create scopes . A scope holds declaration of identifiers , and can prevent name clashing . Defining an identifier with the same name , can be done in different scopes . A scope definition can be expanded .
int xid_i = 1 ; namespace OTHER_ID{ int xid_i = 5 ;} namespace OTHER_ID{ int zid_i = 5 ;} float tax(float money ){ return money * 0.15f ;} namespace irregular{ float tax(float money ){ return money * 0.20f ;}} namespace discounted{ float tax(float money ){ return money * 0.10f ;}} namespace { char d_c = '1'; char l_c = 'u'; } int main(void ){ char var_c = ::d_c; /*The identifier d_c is preceded with the global scope operator :: . d_c is declared in an unnamed namespace . Identifiers in an unnamed namespace belong as accessible to the global scope . Hence the value , of the variable var_c , is the character '1' .*/ var_c = l_c ; /*The identifier l_c is used in the current scope , a lookup for the identifier l_c takes place in the current scope . It is not found , hence the enclosing scope , which is the global scope , is searched . l_c is declared in an unnamed namespace , as such it belongs to the file scope as accessible , hence the value of the variable var_c is the character 'u' .*/ tax(20 ); /*tax is called without using the scope operator . Hence the scope where the tax identifier is used , is first searched , and after that other accessible scopes . The tax function is defined in the global, scope , hence the returned value is 3.0f .*/ irregular::tax(20 ); /*tax is qualified , using the scope operator :: by the namespace irregular , the tax identifier is searched in , the namespace irregular . The tax function defined in irregular , is called . The result is 4.0f .*/ using irregular::tax ; tax(20 ); /*The using declaration , using irregular::tax is used . The using declaration , adds the tax identifier , to the current scope . An identifier with the same name , must not exist in the current scope , or else this will cause an error . tax(20 ) is called , and the scope operator :: is not used , hence the current scope , is searched for the tax identifier . The tax identifier is found in the current scope, this is the identifier added from the namespace irregular , hence the tax function from irregular , is called . The result is 4.0f .*/ using namespace discounted; tax(20 ); /*The using directive , using namespace , does not add the identifiers in the namespace discounted , to the current scope . They are only made accessible . When calling tax , the current scope is searched for the identifier tax . The identifier tax is added , by the using declaration , using irregular::tax , precedingly , to the current scope , hence what is called is the tax function from , the irregular namespace , which returns 4.0f , and not 2.0f .*/ using namespace OTHER_ID; int varID_i = xid_i ; /*This statement will cause , a compiler error to occur . The using directive , using namespace , makes accessible all the identifiers in the namespace OTHER_ID , in the current scope . The identifiers , from the global scope , are also accessible from the current scope . Hence ambiguity , to which identifier is meant arise .*/ }
The class scope
Identifiers declared in a class , have class scope . The scope operator ::
can be used to access a member of a scope . When used with a class , it can be used to access or initialize static variables . It can also be used to access methods of a class , in order to provide a definition .
#include<iostream> class Foo{ /*Declare The class Foo .*/ public : int fct(); /*Declare Foo's public method fct .*/ private : static int var; /*Declare Foo's private static variable var .*/}; int Foo::var = -1; /*Initialize Foo private static variable var .*/ int Foo::fct(){ /*Define Foo function fct .*/ return var;} int main(void ){ Foo foo; std::cout << foo.fct() << std::endl; /*Output : -1 . */}
The local scope
A local scope , starts and ends with the curly brackets {}
. Local scopes can be nested . When defining a function , the function parameters are part of the local scope immediately following its parameters list . Declaration in a for loop , are also part of the local scope immediately following the for loop.
#include<iostream> int add(int x , int y ); /*x , y belong to the function , prototype scope .*/ int main(){ std::cout << add(1 , 3 ) << std::endl; std::cout << add(3 , 1 ) << std::endl ; /*Output : 20 2 .*/} int add(int x , int y ){ int z = 10; /*x , y , z belong to the local scope created after the function parameters list .*/ if(x < y ){ /*The curly brackets create a nested local scope , z belongs to the newly create nested local scope .*/ int z = 20 ; return z ; /*Returns 20 .*/} else { int i = 0 ; /*i Belongs to another newly create nested local scope.*/ for(int i = 2 ; i < 4 ; i ++ ) /*Creates another local scope , i has a starting value of 2 .*/ return i; /*returns 2 */ }}
function scope
Labels declared inside a function , have a function scope . These are the only entities that have functional scope .
#include<iostream> void square_cube( int n ){ int i = 0 ; loop:{ if( i == n ) return ; else if(i % 2 == 0 ) goto powerTwo; else goto powerThree; powerTwo : std::cout << i * i << std::endl; i++; goto loop; powerThree: std::cout << i * i * i << std::endl; i++; goto loop ;}} int main(void ){ square_cube(5 ) ;} /*output 0 1 4 27 16*/
prototype scope
Parameters declared in a prototype declaration have a prototype scope .
bool fit(float weight); /*weight , has prototype scope .*/
How name lookup takes place ?
Unless if qualified using the scope operator ::
, an identifier is searched for starting the point it is used , as such in the scope it is used , the search progress , through the accessible scopes , as described in the previous sections . If qualified , the qualified scope , is searched for the identifier .
In addition to that , for functions which are unqualified , if the name of a function is not found in the scope in which it is used , or in the other accessible scopes , an argument dependent name lookup , also known as Koenig lookup , takes place .
Basically the scopes of the type of the arguments , are searched for the function declaration .
#include<iostream> namespace nmspcFoo{ class ClassFoo{}; void printScopeName(ClassFoo arg ){ std::cout << "nmspcFoo" << std::endl; }} int main(void ){ nmspcFoo::ClassFoo fooVar ; printScopeName(fooVar ) ;} /*Output nmspcFoo .*/