命名空间
变体
操作

作用域

来自 cppreference.cn
< 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 文档中的作用域