Scope
Each name that appears in a C++ program is only valid in some possibly discontiguous portion of the source code called its scope.
Within a scope, unqualified name lookup can be used to associate the name with its declaration.
Block scope
The potential scope of a variable introduced by a declaration in a block (compound statement) begins at the point of declaration and ends at the end of the block. Actual scope is the same as potential scope unless there is a nested block with a declaration that introduces identical name (in which case, the entire potential scope of the nested declaration is excluded from the scope of the outer declaration)
int main() { int a = 0; // scope of the first 'a' begins ++a; // the name 'a' is in scope and refers to the first 'a' { int a = 1; // scope of the second 'a' begins // scope of the first 'a' is interrupted a = 42; // 'a' is in scope and refers to the second 'a' } // block ends, scope of the second 'a' ends // scope of the first 'a' resumes } // block ends, scope of the first 'a' ends int b = a; // Error: name 'a' is not in scope
The potential scope of a name declared in an exception handler begins at the point of declaration and ends when the exception handler ends, and is not in scope in another exception handler or in the enclosing block.
try { f(); } catch(const std::runtime_error& re) { // scope of re begins int n = 1; // scope of n begins std::cout << re.what(); // re is in scope } // scope of re ends, scope of n ends catch(std::exception& e) { std::cout << re.what(); // error: re is not in scope ++n; // error: n is not in scope }
The potential scope of a name declared in the init-statement of the for loop, in the condition of a for loop, in the range_declaration of a range for loop, in the init-statement of the if statement or switch statement (since C++17), in the condition of the if statement, while loop, or switch statement begins at the point of declaration and ends at the end of the controlled statement.
Base* bp = new Derived; if(Derived* dp = dynamic_cast<Derived*>(bp)) { dp->f(); // dp is in scope } // scope of dp ends for(int n = 0; // scope of n begins n < 10; // n is in scope ++n) // n is in scope { std::cout << n << ' '; // n is in scope } // scope of n ends
Function parameter scope
The potential scope of a function parameter (including parameters of a lambda expression) or of a function-local predefined variable begins at its point of declaration.
- If the nearest enclosing function declarator is not the declarator of a function definition, its potential scope ends at the end of that function declarator.
- Otherwise, its potential scope ends at the end of the last exception handler of the function-try-block, or at the end of the function body if a function try block was not used.
const int n = 3; int f1(int n, // scope of global 'n' interrupted, // scope of the parameter 'n' begins int y = n); // error: default argument references a parameter int (*(*f2)(int n))[n]; // OK: the scope of the function parameter 'n' // ends at the end of its function declarator // in the array declarator, global n is in scope // (this declares a pointer to function returning a pointer to an array of 3 int // by contrast auto (*f3)(int n)->int (*)[n]; // error: parameter 'n' as array bound int f(int n = 2) // scope of 'n' begins try // function try block { // the body of the function begins ++n; // 'n' is in scope and refers to the function parameter { int n = 2; // scope of the local variable 'n' begins // scope of function parameter 'n' interrupted ++n; // 'n' refers to the local variable in this block } // scope of the local variable 'n' ends // scope of function parameter 'n' resumes } catch(...) { ++n; // n is in scope and refers to the function parameter throw; } // last exception handler ends, scope of function parameter 'n' ends int a = n; // OK: global 'n' is in scope
Function scope
A label (and only a label) declared inside a function is in scope everywhere in that function, in all nested blocks, before and after its own declaration.
void f() { { goto label; // label in scope even though declared later label:; } goto label; // label ignores block scope } void g() { goto label; // error: label not in scope in g() }
Namespace scope
The potential scope of any entity declared in a namespace begins at the declaration and consists of the concatenation of all namespace definitions for the same namespace name that follow, plus, for any using-directive that introduced this name or its entire namespace into another scope, the rest of that scope.
The top-level scope of a translation unit ("file scope" or "global scope") is also a namespace and is properly called "global namespace scope". The potential scope of any entity declared in the global namespace scope begins at the declaration and continues to the end of the translation unit.
The scope of an entity declared in an unnamed namespace or in an inline namespace includes the enclosing namespace;
namespace N { // scope of N begins (as a member of global namespace) int i; // scope of i begins int g(int a) { return a; } // scope of g begins int j(); // scope of j begins void q(); // scope of q begins namespace { int x; // scope of x begins } // scope of x does not end inline namespace inl { // scope of inl begins int y; // scope of y begins } // scope of y does not end } // scope of i,g,j,q,inl,x,y interrupted namespace { int l=1; // scope of l begins } // scope of l does not end (it's a member of unnamed namespace) namespace N { // scope of i,g,j,q,inl,x,y continues int g(char a) { // overloads N::g(int) return l+a; // l from unnamed namespace is in scope } int i; // error: duplicate definition (i is already in scope) int j(); // OK: repeat function declaration is allowed int j() { // OK: definition of the earlier-declared N::j() return g(i); // calls N::g(int) } int q(); // error: q is already in scope with different return type } // scope of i,g,j,q,inl,x,y interrupted int main() { using namespace N; // scope of i,g,j,q,inl,x,y resumes i = 1; // N::i is in scope x = 1; // N::(anonymous)::x is in scope y = 1; // N::inl::y is in scope inl::y = 2; // N::inl is also in scope } // scope of i,g,j,q,inl,x,y interrupted
Class scope
The potential scope of a name declared in a class begins at the point of declaration and includes the rest of the class body and all function bodies (even if defined outside the class definition or before the declaration of the name), default arguments, exception specifications, in-class brace-or-equal initializers, contract conditions (since C++20), and all these things in nested classes, recursively.
class X { int f(int a = n) { // X::n is in scope inside default parameter return a*n; // X::n is in scope inside function body } int g(); int i = n*2; // X::n is in scope inside initializer // int x[n]; // Error: n is not in scope in class body static const int n = 1; int x[n]; // OK: n is now in scope in class body }; int X::g() { return n; } // X::n is in scope in out-of-class member function body
If a name is used in a class body before it is declared, and another declaration for that name is in scope, the program is ill-formed, no diagnostic required.
typedef int c; // ::c enum { i = 1 }; // ::i class X { char v[i]; // Error: at this point, i refers to ::i // but there is also X::i int f() { return sizeof(c); // OK: X::c, not ::c is in scope inside a member function } char c; // X::c enum { i = 2 }; // X::i }; typedef char* T; struct Y { T a; // error: at this point, T refers to ::T // but there is also Y::T typedef long T; T b; };
Names of any class members can only be used in four contexts:
- in its own class scope or in the class scope of a derived class
- after the
.
operator applied to an expression of the type of its class or a class derived from it - after the
->
operator applied to an expression of the type of pointer to its class or pointers to a class derived from it - after the
::
operator applied to the name of its class or the name of a class derived from it
Enumeration scope
The name of an enumerator introduced in a scoped enumeration begins at the point of declaration and ends at the end of the enum specifier (in contrast, unscoped enumerators are in scope after the end of the enum specifier)
enum e1_t { // unscoped enumeration A, B = A*2 }; // scope of A and B does not end enum class e2_t { // scoped enumeration SA, SB = SA*2 // SA is in scope }; // scope of SA and SB ends e1_t e1 = B; // OK, B is in scope // e2_t e2 = SB; // Error: SB is not in scope e2_t e2 = e2_t::SB; // OK
Template parameter scope
The potential scope of a template parameter name begins immediately at the point of declaration and continues to the end of the smallest template declaration in which it was introduced. In particular, a template parameter can be used in the declarations of subsequent template parameters and in the specifications of base classes, but can't be used in the declarations of the preceding template parameters.
template< typename T, // scope of T begins T* p, // T can be used for a non-type parameter class U = T // T can be used for a default type > class X : public Array<T> // T can be used in base class name { // T can be used inside the body as well }; // scopes of T and U end, scope of X continues
The potential scope of the name of the parameter of a template template parameter is the smallest template parameter list in which that name appears
template< template< // template template parameter typename Y, // scope of Y begins typename G = Y // Y is in scope > // scopes of Y and G end class T, // typename U = Y // Error: Y is not in scope typename U > class X { }; // scopes of T and U end
Similar to other nested scopes, the name of a template parameter hides the same name from the outer scope for the duration of its own:
typedef int N; template< N X, // non-type parameter of type int typename N, // scope of this N begins, scope of ::N interrupted template<N Y> class T // N here is the template parameter, not int > struct A;
Point of declaration
Scope begins at the point of declaration, which is located as follows:
For variables and other names introduced by simple declarations, the point of declaration is immediately after that name's declarator and before its initializer, if any:
unsigned char x = 32; // scope of the first 'x' begins { unsigned char x = x; // scope of the second 'x' begins before the initializer (= x) // this does not initialize the second 'x' with the value 32, // this initializes the second 'x' with its own, // indeterminate, value } std::function<int(int)> f = [&](int n){return n>1 ? n*f(n-1) : n;}; // the name of the function 'f' is in scope within the lambda, and can // be correctly captured by reference, giving a recursive function
const int x = 2; // scope of the first 'x' begins { int x[x] = {}; // scope of the second x begins before the initializer (= {}) // but after the declarator (x[x]). Within the declarator, the outer // 'x' is still in scope. This declares an array of 2 int. }
The point of declaration of a structured binding is immediately after the identifier-list of the structured binding declaration, but structured binding initializers are prohibited from referring to any of the names being introduced. |
(since C++17) |
The point of declaration of a class or template is immediately after the identifier that names the class (or the template-id that names the template specialization) appears in its class-head, and is already in scope in the list of the base classes:
// the name 'S' is in scope immediately after it appears, // so it can be used in the list of base classes struct S: std::enable_shared_from_this<S> { };
The point of declaration of an enumeration is immediately after the identifier that names it appears in the enum specifier or opaque enum declaration, whichever is used first:
enum E : int { // E is already in scope A = sizeof(E) };
The point of declaration of a type alias or alias template is immediately after the type-id to which the alias refers:
using T = int; // point of declaration of T is at the semicolon using T = T; // same as T = int
The point of declaration of an enumerator is immediately after its definition (not before the initializer as it is for variables):
const int x = 12; { enum { x = x + 1, // point of declaration is at the comma, x is initialized to 13 y = x + 1 // the enumerator x is now in scope, y is initialized to 14 }; }
The point of declaration for an injected-class-name is immediately following the opening brace of its class (or class template) definition
template<typename T> struct Array // : std::enable_shared_from_this<Array> // Error: the injected class name is not in scope : std::enable_shared_from_this< Array<T> > //OK: the template-name Array is in scope { // the injected class name Array is now in scope as if a public member name Array* p; // pointer to Array<T> };
This section is incomplete Reason: remainder of [basic.scope.pdecl] |
References
- C++11 standard (ISO/IEC 14882:2011):
- 3.3 Scope [basic.scope]
- C++98 standard (ISO/IEC 14882:1998):
- 3.3 Declarative regions and scopes [basic.scope]