作用域
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 文件中的作用域
|