名稱空間
變體
操作

作用域

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
表示式
替代表示
字面量
布林字面量 - 整數字面量 - 浮點字面量
字元字面量 - 字串字面量 - nullptr (C++11)
使用者定義 (C++11)
工具
屬性 (C++11)
型別
typedef 宣告
類型別名宣告 (C++11)
型別轉換
記憶體分配
類特有的函式屬性
explicit (C++11)
static

特殊成員函式
模板
雜項
 
 

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 處的包圍作用域。
  • 全域性作用域包含作用域 ST,並且作用域 S 包含作用域 T
  • 因此,作用域 T 是這三者中最小的作用域,這意味著
  • 作用域 T 是程式點 Y 處的直接作用域。
  • 變數 y 的宣告在其位置處存在於作用域 T 中。
  • 作用域 Ty 宣告的目標作用域。
  • 變數 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函式引數作用域

  • 如果函式宣告是函式定義,則引入的作用域延伸到函式定義的末尾。
  • 否則(函式宣告是函式原型),引入的作用域延伸到函式宣告符的末尾。
  • 在這兩種情況下,該作用域不包含函式宣告的位置
  • 如果宣告的引數屬於lambda 表示式的引數列表,則引入的作用域延伸到 { body } 的末尾。
(C++11 起)
  • 如果宣告的引數屬於推導指引的引數列表,則引入的作用域延伸到該推導指引的末尾。
(C++17 起)
  • 如果宣告的引數屬於requires 表示式的引數列表,則引入的作用域延伸到 { requirement-seq } 的末尾。
(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 作用域,該作用域緊隨 [captures ] 之後開始,並延伸到 { body } 的末尾。

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 中:

  • 對於(模板)重宣告或類模板特化,是其類頭名稱之後的部分。
  • 對於列舉重宣告,是其列舉頭名稱之後的部分。
  • 對於任何其他重宣告或特化,是宣告符非限定 ID限定 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類定義成員說明

對於每個目標作用域為 S 或包含於 S 的非友元重宣告或特化,以下部分也包含在作用域 S 中:

  • 對於(模板)重宣告或類模板特化,是其類頭名稱之後的部分。
  • 對於列舉重宣告,是其列舉頭名稱之後的部分。
  • 對於任何其他重宣告或特化,是宣告符非限定 ID限定 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 起)列舉宣告列舉器列表(如果存在)。

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”
}

契約斷言作用域

每個契約斷言 C 都會引入一個包含 C契約斷言作用域

如果帶有識別符號後置條件斷言不是與名稱無關的,並且該後置條件斷言與函式 func 關聯,可能與目標作用域為以下作用域之一的宣告 D 衝突,則程式非良構:

  • 函式 func 的函式引數作用域。
  • 如果 Dlambda 表示式關聯,則為前置條件斷言的最近的包圍 lambda 作用域。
(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 起)

命名名稱空間定義的位置緊隨名稱空間名稱之後。

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 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 文件中的作用域