Each name that appears in a C++ program is only visible 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.
The potential scope of a name declared 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 an identical name is declared in a nested block, in which case the potential scope of the name in the nested block is excluded from the actual scope of the name in the enclosing block.
int main() { int i = 0; // scope of outer i begins ++i; // outer i is in scope { int i = 1; // scope of inner i begins, // scope of outer i pauses i = 42; // inner i is in scope } // block ends, scope of inner i ends, // scope of outer i resumes } // block ends, scope of outer i ends // int j = i; // error: i is not in scope
The potential scope of a name declared in an exception handler begins at the point of declaration and ends at the end of the exception handler, 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 << n << 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 a for loop, in the condition of a for loop, in the range_declaration of a range-for loop, in the init-statement of an if statement or switch statement (since C++17), in the condition of an if statement, switch statement, or while loop 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
The potential scope of a name declared in a function parameter (including parameters of a lambda expression) or of a function-local predefined variable begins at the point of declaration.
const int n = 3; int f1( int n // scope of function parameter n begins, // scope of global n pauses // , int y = n // error: default argument references a function parameter ); int (*(*f2)(int n))[n]; // OK: scope of function parameter n // ends at the end of its function declarator // in the array declarator, global n is in scope // declares a pointer to function returning a pointer to an array of 3 int //auto (*f3)(int n)->int (*)[n]; // error: function parameter n as array bound void f(int n = 2) // scope of function parameter n begins try // function try block { // function body begins ++n; // function parameter n is in scope { int n = 2; // scope of local n begins // scope of function parameter n pauses ++n; // local n is in scope } // scope of local n ends // scope of function parameter n resumes } catch (std::exception& e) { ++n; // function parameter n is in scope throw; } // last exception handler ends, scope of function parameter n ends int a = n; // global n is in scope
The potential scope of a name declared in a namespace begins at the point of declaration and includes the rest of the namespace and all namespace definitions with an identical 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 a name declared in the global namespace scope begins at the point of declaration and ends at the end of the translation unit.
The potential scope of a name declared in an unnamed namespace or in an inline namespace includes the potential scope that name would have if it were declared in 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 continues (member of unnamed namespace) inline namespace inl { // scope of inl begins int y; // scope of y begins } // scope of y continues (member of inline namespace) } // scopes of i, g, j, q, inl, x, and y pause namespace { int l = 1; // scope of l begins } // scope of l continues (member of unnamed namespace) namespace N { // scopes of i, g, j, q, inl, x, and y resume 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: duplicate 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 a different return type } // scopes of i, g, j, q, inl, x, and y pause int main() { using namespace N; // scopes of i, g, j, q, inl, x, and y resume 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 } // scopes of i, g, j, q, inl, x, and y end
The name may also be visible in translation units that have imported the current translation unit. | (since C++20) |
The potential scope of a name declared in a class begins at the point of declaration and includes the rest of the class body, all the derived classes bodies, the function bodies (even if defined outside the class definition or before the declaration of the name), function default arguments, function exception specifications, in-class brace-or-equal initializers, and all these things in nested classes, recursively.
struct X { int f(int a = n) { // n is in scope in function default argument return a * n; // n is in scope in function body } using r = int; r g(); int i = n * 2; // n is in scope in initializer // int x[n]; // error: n is not in scope in class body static const int n = 1; // scope of n begins int x[n]; // OK: n is now in scope in class body }; // scope of n pauses struct Y: X { // scope of n resumes int y[n]; // n is in scope }; // scope of n ends //r X::g() { // error: r is not in scope outside out-of-class function body auto X::g()->r { // OK: trailing return type r is in scope return n; // n is in scope in out-of-class 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 is in scope in member function } enum { i = 2 }; // X::i char c; // X::c char w[i]; // OK: i refers to X::i now }; // scope of outer i resumes 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 class members can be used in the following contexts:
.
operator applied to an expression of the type of its class or a class derived from it; ->
operator applied to an expression of the type of pointer to its class or pointers to a class derived from it; ::
operator applied to the name of its class or the name of a class derived from it. The potential scope of an enumerator of an unscoped enumeration begins at the point of declaration and ends at the end of the enclosing scope.
The potential scope of an enumerator of a scoped enumeration begins at the point of declaration and ends at the end of the enum specifier.
enum e1_t { // unscoped enumeration A, B = A * 2 // A is in scope }; // scopes of A and B continue enum class e2_t { // scoped enumeration SA, SB = SA * 2 // SA is in scope }; // scopes of SA and SB end 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
The potential scope of a template parameter name begins at the point of declaration and ends at 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 is in scope class U = T // T is in scope > class X: public std::vector<T> // T is in scope { T f(); // T is in scope }; // scopes of T and U end
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 enclosing scope for the duration of its own.
typedef int N; template< N X, // ::N is in scope typename N, // scope of N begins, scope of ::N pauses template<N Y> class T // N is in scope > struct A; // scope of N ends, scope of ::N resumes
In general, a name is visible after the locus of its first declaration, which is located as follows.
The locus of a name declared in a simple declaration is immediately after that name's declarator and before its initializer, if any.
unsigned char x = 32; // outer x is in scope { unsigned char x = x; // inner x is in scope before the initializer (= x) // this does not initialize inner x with the value of outer x, // this initializes inner 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 in the lambda and can // be correctly captured by reference, giving a recursive function
const int x = 2; // outer x is in scope { int x[x] = {}; // inner x is in scope before the initializer (= {}), // but after the declarator (x[x]) // in the declarator, outer x is still in scope // this declares an array of 2 int }
The locus of a class or class template declaration is immediately after the identifier that names the class (or the template-id that names the template specialization) in its class-head. The class or class template name is already in scope in the list of base classes.
struct S: std::enable_shared_from_this<S> // S is in scope at the colon {};
The locus of enum specifier or opaque enum declaration is immediately after the identifier that names the enumeration.
enum E: int { // E is in scope at the colon A = sizeof(E) };
The locus of a type alias or alias template declaration is immediately after the type-id to which the alias refers.
using T = int; // outer T is in scope at the semicolon { using T = T*; // inner T is in scope at the semicolon, // outer T is still in scope before the semicolon // same as T = int* }
The locus for a declarator in a using declaration that does not name a constructor is immediately after the declarator.
template<int N> class Base { protected: static const int next = N + 1; static const int value = N; }; struct Derived: Base<0>, Base<1>, Base<2> { using Base<0>::next, // next is in scope at the comma Base<next>::value; // Derived::value is 1 };
The locus 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, // enumerator x is in scope at the comma, // outer x is in scope before the comma, // enumerator x is initialized to 13 y = x + 1 // y is initialized to 14 }; }
The locus 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> };
The locus of the implicit declaration for a function-local predefined variable | (since C++11) |
The locus of a structured binding declaration is immediately after the identifier-list, but structured binding initializers are prohibited from referring to any of the names being declared. | (since C++17) |
The locus of the variable or the structured bindings (since C++17) declared in the range_declaration of a range-for loop is immediately after the range_expression. std::vector<int> x; for (auto x: x) { // auto x is in scope at the closing parenthesis, // vector x is in scope before the closing parenthesis // the auto x is in scope } | (since C++11) |
The locus of a template parameter is immediately after its complete template parameter (including the optional default argument).
typedef unsigned char T; template< class T = T, // template parameter T is in scope at the comma, // typedef name of unsigned char is in scope before the comma T // template parameter T is in scope N = 0 > struct A { };
The locus of a concept definition is immediately after the concept name, but concept definitions are prohibited from referring to the concept name being declared. | (since C++20) |
The locus of a named namespace definition is immediately after the namespace name.
C documentation for Scope |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/scope