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 scopeThe 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 endsThe 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 scopeThe 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; // OKThe 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 endThe 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 endSimilar 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 resumesIn 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 functionconst 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