作用域
C++ 程序中的每个声明只在某些可能不连续的作用域中可见。
在一个作用域内,非限定名称查找可以用于将名称与其声明关联起来。
目录 |
[编辑] 概述
每个程序都有一个全局作用域,它包含整个程序。
所有其他作用域 S
由以下之一引入:
|
(C++26 起) |
S
总是出现在另一个作用域中,从而该作用域包含 S
。
程序点处的包围作用域是包含它的任何作用域;其中最小的作用域被称为该点处的直接作用域。
如果一个作用域 S
(不包含程序点 P
)是或包含 S
但不包含 P
,则它在程序点 P
和作用域 S
之间介入。
任何非模板参数作用域的作用域 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
如果声明存在于块作用域 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” }
[编辑] 函数参数作用域
每个参数声明 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
中:
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
的类定义的成员说明。
对于每个目标作用域为 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
[编辑] 枚举作用域
枚举 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 }
[编辑] 模板参数作用域
每个模板模板参数都会引入一个模板参数作用域,该作用域包含该模板模板参数的整个模板参数列表和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 起) |
[编辑] 声明点
通常,名称在其第一次声明的位置之后可见,位置确定如下。
在简单声明中声明的名称的位置紧随该名称的声明符之后,如果有初始化器,则在初始化器之前。
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 起) |
结构化绑定声明的位置紧随标识符列表之后,但结构化绑定初始化器禁止引用任何正在声明的名称。 |
(C++17 起) |
range-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] 的其余部分 |
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
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 文档中的作用域
|