命名空间
变体
操作

作用域

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++17* 中弃用)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

每个出现在 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 上的外围作用域。
  • 全局作用域包含作用域 ST,作用域 S 包含作用域 T
  • 因此,作用域 T 是所有三个作用域中最小的作用域,这意味着
  • 作用域 T 是程序点 Y 上的直接作用域。
  • 变量 y 的声明驻留在其位置的作用域 T 中。
  • 作用域 Ty 的声明的目标作用域。
  • 变量 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

  • 如果声明的形参是函数声明的形参列表的形参
  • 如果函数声明是函数定义,则引入的作用域将扩展到函数定义的末尾。
  • 否则(函数声明是函数原型),引入的作用域将扩展到函数声明符的末尾。
  • 在这两种情况下,作用域都不包括函数声明的位置
  • 如果声明的形参是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 起)

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

契约断言作用域

每个契约断言 C 都会引入一个契约断言作用域,其中包含 C

如果后置条件断言具有不是与名称无关的 标识符,并且后置条件断言与函数 func 相关联,可能与目标作用域是以下作用域之一的声明 D 冲突,则程序是非良构的

  • func 的函数形参作用域。
  • 如果 Dlambda 表达式相关联,则为前置条件断言的最近外围 lambda 作用域。
(自 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 起)

具名命名空间定义的位置紧随命名空间名称之后。

[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 文档 关于 作用域