范围
每个出现在 C++ 程序中的声明仅在某些可能是不连续的范围中可见。
在某个范围内,可以使用非限定名称查找将名称与其声明关联起来。
内容 |
[编辑] 一般
每个程序都有一个全局范围,它包含整个程序。
每个其他范围S
由以下内容引入
S
总是出现在另一个范围内,因此该范围包含S
。
程序点处的封闭范围是任何包含它的范围;最小的范围被称为该点的直接范围。
如果某个范围介于程序点P
和范围S
(不包含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
之间。
[编辑] 块范围
每个
引入一个包含该语句或处理程序的块作用域。
属于块作用域的变量称为块变量。
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
块作用域的 extern 声明目标于更大的封闭作用域,但在其直接作用域中绑定名称。
如果一个声明 不是 与名称无关的声明 并且(自 C++26 起) 在块作用域 S
中绑定名称,该块作用域 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” }
[编辑] 函数参数作用域
每个 参数声明 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 起) |
[编辑] 命名空间作用域
每个命名空间 N
的 命名空间定义 引入一个命名空间作用域 S
,该作用域包含 N
的每个命名空间定义的 声明。
对于每个非友元重新声明或特化,其目标作用域为 S
或包含在 S
中,以下部分也包含在作用域 S
中
- 对于 类(模板)重新声明或类模板特化,其后的部分为 class-head-name。
- 对于 枚举 重新声明,其后的部分为 enum-head-name。
- 对于任何其他重新声明或特化,其后的部分为 声明符 的 unqualified-id 或 qualified-id。
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
[编辑] 类作用域
每个类或类模板 C
的声明引入一个类作用域 S
,该作用域包含 C
的 类定义 的 member-specification。
对于每个非友元重新声明或特化,其目标作用域为 S
或包含在 S
中,以下部分也包含在作用域 S
中
- 对于 类(模板)重新声明或类模板特化,其后的部分为 class-head-name。
- 对于 枚举 重新声明,其后的部分为 enum-head-name。
- 对于任何其他重新声明或特化,其后的部分为 声明符 的 unqualified-id 或 qualified-id。
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
[编辑] 枚举作用域
每个枚举 E
的声明引入一个枚举作用域,该作用域包含 E
的 非不透明的(自 C++11 起) 枚举声明 的 enumerator-list(如果存在)。
enum class E // the enumeration declaration of “E” { // introduces an enumeration scope “S” // scope “S” begins here e1, e2, e3 // scope “S” ends here }
[编辑] 模板参数作用域
每个 模板模板参数 引入一个模板参数作用域,该作用域包含该模板模板参数的整个模板参数列表 和 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
[编辑] 声明点
通常,名称在其第一个声明的位置之后可见,该位置如下确定。
简单声明中声明的名称的位置位于该名称的 声明符 之后,位于其初始化器之前(如果有)。
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 }
类或类模板声明的位置位于其 类头部 中命名该类的标识符(或命名该模板特化的 template-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) };
类型别名或别名模板 声明的位置位于别名引用的 type-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 起) |
结构化绑定声明 的位置位于 identifier-list 之后,但结构化绑定初始化器禁止引用任何正在声明的名称。 |
(自 C++17 起) |
范围-for 循环 的 range-declaration 中声明的变量 或结构化绑定(自 C++17 起) 的位置位于 range-expression 之后。 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++20 起) |
命名 命名空间定义 的位置位于命名空间名称之后。
本节不完整 原因:[basic.scope.pdecl] 的其余部分 |
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布的行为 | 正确行为 |
---|---|---|---|
CWG 2793 | C++98 | 块作用域中的 extern 声明可能 与父作用域中的另一个声明冲突 |
禁止 |
[编辑] 参考文献
- 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]
[编辑] 另请参阅
C 文档 的 作用域
|