作用域
每个出现在 C++ 程序中的声明仅在某些可能不连续的作用域中可见。
在作用域内,可以使用非限定名查找将名称与其声明关联起来。
目录 |
[edit] 概述
每个程序都具有全局作用域,它包含整个程序。
每个其他作用域 S
均由以下之一引入
(自 C++26 起) |
S
始终出现在另一个作用域中,该作用域因此包含 S
。
程序点上的外围作用域是包含它的任何作用域;最小的此类作用域被称为该点上的直接作用域。
如果作用域是 S
或包含 S
但不包含 P
,则该作用域介入程序点 P
和作用域 S
(不包含 P
)之间。
任何非模板形参作用域的作用域 S
的父作用域是包含 S
且不是模板形参作用域的最小作用域。
除非另有规定
- 声明驻留在其位置的直接作用域中。
- 声明的目标作用域是它驻留的作用域。
- 由声明(重新)引入的任何名称都在其目标作用域中绑定到它。
如果 S
是实体的声明的目标作用域,则实体属于作用域 S
。
// global scope scope // scope S T int x; // ─┐ // program point X // │ { // │ ─┐ { // │ │ ─┐ int y; // │ │ │ // program point Y } // │ │ ─┘ } // ─┘ ─┘
在上面的程序中
- 全局作用域、作用域
S
和作用域T
包含程序点Y
。
- 换句话说,这三个作用域都是程序点
Y
上的外围作用域。
- 换句话说,这三个作用域都是程序点
- 全局作用域包含作用域
S
和T
,作用域S
包含作用域T
。
- 因此,作用域
T
是所有三个作用域中最小的作用域,这意味着
- 作用域
T
是程序点Y
上的直接作用域。 - 变量 y 的声明驻留在其位置的作用域
T
中。 - 作用域
T
是 y 的声明的目标作用域。 - 变量 y 属于作用域
T
。
- 作用域
- 作用域
S
是作用域T
的父作用域,全局作用域是作用域S
的父作用域。
- 因此,作用域
- 作用域
S
介入程序点X
和作用域T
之间。
[edit] 块作用域
每个
引入包含语句或处理程序的块作用域。
属于块作用域的变量是块变量。
int i = 42; int a[10]; for (int i = 0; i < 10; i++) // inner “i” inhabits the block scope a[i] = i; // introduced by the for-statement int j = i; // j = 42
如果声明驻留在块作用域 S
中,并且声明函数或使用 extern 说明符,则该声明不得附加到具名模块(自 C++20 起),其目标作用域是更大的外围作用域(最内层的外围命名空间作用域),但名称在其直接作用域 S
中绑定。
如果声明不是与名称无关的声明并且(自 C++26 起)在以下各项的块作用域 S
中绑定名称
|
(自 C++11 起) |
- 本身不是选择语句或迭代语句的选择语句或迭代语句的子语句,或
- 函数 try 块的处理程序
可能与目标作用域是 S
的父作用域的声明冲突,则程序是非良构的。
if (int x = f()) // declares “x” { // the if-block is a substatement of the if-statement int x; // error: redeclaration of “x” } else { // the else-block is also a substatement of the if-statement int x; // error: redeclaration of “x” } void g(int i) { extern int i; // error: redeclaration of “i” }
[edit] 函数形参作用域
每个形参声明 P
都会引入一个函数形参作用域,其中包含 P
。
- 如果声明的形参是函数声明的形参列表的形参
|
(自 C++11 起) |
|
(自 C++17 起) |
|
(自 C++20 起) |
int f(int n) // the declaration of the parameter “n” { // introduces a function parameter scope /* ... */ } // the function parameter scope ends here
Lambda 作用域每个lambda 表达式都会引入一个lambda 作用域,该作用域在 lambda 表达式 E 的带有初始化器的捕获驻留在 E 引入的 lambda 作用域中。 auto lambda = [x = 1, y]() // this lambda expression introduces a lambda scope, { // it is the target scope of capture “x” /* ... */ }; // the lambda scope ends before the semicolon |
(自 C++14 起) |
[edit] 命名空间作用域
命名空间 N
的每个命名空间定义都引入一个命名空间作用域 S
,其中包含 N
的每个命名空间定义的 声明。
对于目标作用域为 S
或由 S
包含的每个非友元重声明或特化,以下部分也包含在作用域 S
中
namespace V // the namespace definition of “V” { // introduces a namespace scope “S” // the first part of scope “S” begins here void f(); // the first part of scope “S” ends here } void V::f() // the portion after “f” is also a part of scope “S” { void h(); // declares V::h } // the second part of scope “S” ends here
[edit] 类作用域
类或类模板 C
的每个声明都会引入一个类作用域 S
,其中包含 C
的类定义的 成员规范。
对于目标作用域为 S
或由 S
包含的每个非友元重声明或特化,以下部分也包含在作用域 S
中
class C // the class definition of “C” { // introduces a class scope “S” // the first part of scope “S” begins here void f(); // the first part of scope “S” ends here } void C::f() // the portion after “f” is also a part of scope “S” { /* ... */ } // the second part of scope “S” ends here
[edit] 枚举作用域
枚举 E
的每个声明都会引入一个枚举作用域,其中包含 E
的非不透明(自 C++11 起) 枚举声明的 枚举符列表(如果存在)。
enum class E // the enumeration declaration of “E” { // introduces an enumeration scope “S” // scope “S” begins here e1, e2, e3 // scope “S” ends here }
[edit] 模板形参作用域
每个模板模板形参都会引入一个模板形参作用域,其中包含该模板模板形参的整个模板形参列表和require 子句(自 C++20 起)。
每个模板声明 D
都会引入一个模板形参作用域 S
,该作用域从 D
的模板形参列表的开头扩展到 D
的末尾。模板形参列表之外的任何声明,如果本应驻留在 S
中,则改为驻留在与 D
相同的作用域中。
只有模板形参属于模板形参作用域,并且只有模板形参作用域具有模板形参作用域作为父作用域。
// the class template declaration of “X” // introduces a template parameter scope “S1” template < // scope “S1” begins here template // the template template parameter “T” // introduces another template parameter scope “S2” < typename T1 typename T2 > requires std::convertible_from<T1, T2> // scope “S2” ends here class T, typename U > class X; // scope “S1” ends before the semicolon namespace N { template <typename T> using A = struct X; // “X” inhabits the same scope as template // declaration, namely the scope of “N” }
契约断言作用域每个契约断言 如果后置条件断言具有不是与名称无关的 标识符,并且后置条件断言与函数 func 相关联,可能与目标作用域是以下作用域之一的声明
|
(自 C++26 起) |
[edit] 声明点
通常,名称在其首次声明的位置之后可见,该位置如下。
在简单声明中声明的名称的位置紧随该名称的声明符之后,并在其初始化器(如果有)之前。
int x = 32; // outer x is in scope { int x = x; // inner x is in scope before the initializer (= x) // this does not initialize inner x with the value of outer x (32), // 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 }
类或类模板声明的位置紧随其类头中命名该类的标识符(或命名模板特化的模板 ID)之后。类或类模板名称已在基类列表中处于作用域中。
struct S: std::enable_shared_from_this<S> {}; // S is in scope at the colon
枚举说明符或不透明枚举声明(自 C++11 起)的位置紧随命名枚举的标识符之后。
enum E : int // E is in scope at the colon { A = sizeof(E) };
类型别名或别名模板声明的位置紧随别名引用的类型 ID 之后。
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* }
不命名构造函数的using 声明中的声明符的位置紧随声明符之后。
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 };
枚举符的位置紧随其定义之后(而不是像变量那样在初始化器之前)。
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 }; }
注入类名称的位置紧随其类(或类模板)定义的左花括号之后。
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> };
函数局部预定义变量 __func__ 的隐式声明的位置紧随函数定义的函数体之前。 |
(自 C++11 起) |
结构化绑定声明的位置紧随 标识符列表 之后,但禁止结构化绑定初始化器引用正在声明的任何名称。 |
(自 C++17 起) |
在 范围 for 循环的 范围声明 中声明的变量或结构化绑定(自 C++17 起)的位置紧随 范围表达式 之后。 std::vector<int> x; for (auto x : x) // vector x is in scope before the closing parenthesis, // auto x is in scope at the closing parenthesis { // the auto x is in scope } |
(自 C++11 起) |
模板形参的位置紧随其完整的模板形参之后(包括可选的默认实参)。
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 { };
具有 标识符 的后置条件断言的位置紧随其 |
(自 C++26 起) |
概念定义的位置紧随概念名称之后,但禁止概念定义引用正在声明的概念名称。 |
(自 C++20 起) |
具名命名空间定义的位置紧随命名空间名称之后。
本节不完整 原因:[basic.scope.pdecl] 的剩余部分 |
[edit] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 2793 | C++98 | 块作用域中的 extern 声明可能 与父作用域中的另一个声明冲突 |
被禁止 |
[edit] 参考
- C++23 标准 (ISO/IEC 14882:2024)
- 6.4 作用域 [basic.scope]
- C++20 标准 (ISO/IEC 14882:2020)
- 6.4 作用域 [basic.scope]
- C++17 标准 (ISO/IEC 14882:2017)
- 6.3 作用域 [basic.scope]
- C++14 标准 (ISO/IEC 14882:2014)
- 3.3 作用域 [basic.scope]
- C++11 标准 (ISO/IEC 14882:2011)
- 3.3 作用域 [basic.scope]
- C++98 标准 (ISO/IEC 14882:1998)
- 3.3 声明区域和作用域 [basic.scope]
[edit] 参见
C 文档 关于 作用域
|