Namespaces
Variants

Scope

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

declaration は、C++プログラム内で現れる場合、いくつかの非連続的な可能性がある scope でのみ可視となります。

スコープ内では、 unqualified name lookup を使用して、名前をその宣言に関連付けることができます。

目次

一般

各プログラムには グローバルスコープ があり、これはプログラム全体を 含みます

他のすべてのスコープ S は以下のいずれかによって導入されます:

(C++26以降)

S は常に別のスコープ内に現れ、それによってそのスコープが 包含する S となります。

プログラムポイントにおける enclosing scope(エンクロージングスコープ) とは、そのポイントを含む任意のスコープのことです。そのようなスコープの中で最小のものを、そのポイントにおける immediate scope(イミディエイトスコープ) と呼びます。

スコープは、プログラムポイント P とスコープ S P を含まない)の間に 介在する とは、それが S であるか S を含むが、 P を含まない場合を指します。

テンプレートパラメータスコープではない任意のスコープ S parent scope は、 S を含み、かつテンプレートパラメータスコープではない最小のスコープです。

特に指定がない限り:

  • 宣言はその 宣言点 において直近のスコープを 占有する
  • 宣言の ターゲットスコープ は、その宣言が占有するスコープである。
  • 宣言によって(再)導入される名前は、そのターゲットスコープ内でその宣言に 束縛される

エンティティがスコープ S 属する のは、 S がそのエンティティの宣言のターゲットスコープである場合です。

//                グローバル  スコープ  スコープ
//                スコープ      S        T
int x;         //   ─┐                 // プログラムポイント X
               //    │
{              //    │     ─┐
    {          //    │      │     ─┐
        int y; //    │      │      │   // プログラムポイント Y
    }          //    │      │     ─┘
}              //   ─┘     ─┘

上記のプログラムにおいて:

  • グローバルスコープ、スコープ S およびスコープ T はプログラムポイント Y を含みます。
  • 言い換えれば、これら3つのスコープはすべてプログラムポイント Y における囲みスコープです。
  • グローバルスコープはスコープ S T を含み、スコープ S はスコープ T を含みます。
  • したがって、スコープ T は3つの中で最も小さいスコープであり、つまり:
  • スコープ T はプログラムポイント Y における直接のスコープです。
  • 変数 y の宣言はその位置でスコープ T に属します。
  • スコープ T y の宣言のターゲットスコープです。
  • 変数 y はスコープ T に属します。
  • スコープ S はスコープ T の親スコープであり、グローバルスコープはスコープ S の親スコープです。
  • スコープ S はプログラムポイント X とスコープ T の間に介在します。

ブロックスコープ

block scope を持つ ブロックスコープ を導入し、その中に文またはハンドラを含みます。

ブロックスコープに属する変数は ブロック変数 (ローカル変数とも呼ばれます)です。

int i = 42;
int a[10];
for (int i = 0; i < 10; i++) // 内側の「i」はfor文によって導入されたブロックスコープに属する
    a[i] = i;                // for文によって導入された
int j = i; // j = 42

宣言がブロックスコープ S 内に存在し、関数を宣言するか extern 指定子を使用する場合 、その宣言は 名前付きモジュール  に付属してはならず (C++20以降) 、そのターゲットスコープはより大きな外側のスコープ(最も内側の外側の名前空間スコープ)ですが、名前はその直接のスコープ S 内にバインドされます。

宣言が name-independent宣言ではなく (C++26以降) ブロックスコープ S 内の名前をバインドする場合

  • lambda式 の複合ステートメント { body }
(C++11以降)
  • 選択文または反復文の一部であり、それ自体が選択文または反復文ではない副文、または
  • 関数の try ブロックのハンドラ

潜在的に競合する 宣言が S の親スコープをターゲットスコープとする場合、プログラムは不適格となります。

if (int x = f())  // 「x」を宣言
{ // ifブロックはif文の副文
    int x;        // エラー: 「x」の再宣言
}
else
{ // elseブロックもif文の副文
    int x;        // エラー: 「x」の再宣言
}
void g(int i)
{
    extern int i; // エラー: 「i」の再宣言
}

関数パラメータのスコープ

パラメータ宣言 P は、 P を含む 関数パラメータスコープ を導入します。

  • 宣言されたパラメータが 関数宣言 のパラメータリストに属する場合:
  • 関数宣言が 関数定義 である場合、導入されたスコープは関数定義の終端まで拡張されます。
  • それ以外の場合(関数宣言が関数プロトタイプである場合)、導入されたスコープは関数宣言子の終端まで拡張されます。
  • 両方の場合において、スコープは関数宣言の 宣言点 を含みません。
  • 宣言されたパラメータが lambda expression のパラメータリストに属する場合、導入されたスコープは { body } の終わりまで拡張されます。
(C++11以降)
  • 宣言されたパラメータが deduction guide のパラメータリストに属する場合、導入されたスコープはそのdeduction guideの終わりまで拡張されます。
(C++17以降)
  • 宣言されたパラメータが requires expression のパラメータリストに属する場合、導入されたスコープは { requirement-seq } の終わりまで拡張されます。
(C++20以降)
int f(int n) // パラメータ「n」の宣言
{            // 関数パラメータスコープの開始
    /* ... */
}            // 関数パラメータスコープはここで終了

Lambda スコープ

ラムダ式 は、 ラムダスコープ を導入します。このスコープは [ キャプチャ  ] の直後から始まり、 { 本体 } の終わりまで拡張されます。

ラムダ式 E キャプチャ と初期化子は、 E によって導入されたラムダスコープ内に存在します。

auto lambda = [x = 1, y]() // このラムダ式はラムダスコープを導入し、
{                          // キャプチャ「x」のターゲットスコープとなります
    /* ... */
};                         // ラムダスコープはセミコロンの前で終了します
(C++14以降)

名前空間スコープ

すべての namespace definition は、namespace N に対して namespace scope S を導入し、これは N に対するすべてのnamespace definitionの declarations を含みます。

フレンドでない再宣言または特殊化のそれぞれについて、そのターゲットスコープが S であるか、または S に包含されている場合、以下の部分もスコープ S に含まれます:

  • クラス(テンプレート)の再宣言またはクラステンプレートの特殊化の場合、 class-head-name 以降の部分。
  • 列挙型の再宣言の場合、 enum-head-name 以降の部分。
  • その他の再宣言または特殊化の場合、 宣言子 unqualified-id または qualified-id 以降の部分。

グローバルスコープ グローバル名前空間 の名前空間スコープです。

namespace V   // 名前空間「V」の定義
{             // 名前空間スコープ「S」を導入
    // スコープ「S」の最初の部分がここから始まる
    void f();
    // スコープ「S」の最初の部分がここで終わる
}
void V::f()   // 「f」以降の部分もスコープ「S」の一部
{
    void h(); // V::hを宣言
}             // スコープ「S」の2番目の部分がここで終わる

クラススコープ

クラスまたはクラステンプレート C の各宣言は、 クラススコープ S を導入します。このスコープは、 member-specification を含み、 クラス定義 C に属します。

フレンドではない再宣言または特殊化のそれぞれについて、そのターゲットスコープが S であるか、または S に包含されている場合、以下の部分もスコープ S に含まれます:

  • クラス(テンプレート)の再宣言またはクラステンプレートの特殊化の場合、 class-head-name 以降の部分。
  • 列挙型の再宣言の場合、 enum-head-name 以降の部分。
  • その他の再宣言または特殊化の場合、 宣言子 unqualified-id または qualified-id 以降の部分。
class C       // 「C」のクラス定義
{             // クラススコープ「S」を導入
    // スコープ「S」の最初の部分がここから始まる
    void f();
    // スコープ「S」の最初の部分がここで終わる
}
void C::f()   // 「f」以降の部分もスコープ「S」の一部
{
    /* ... */
}             // スコープ「S」の2番目の部分がここで終わる

列挙型のスコープ

列挙型 E の各宣言は、 E 列挙型宣言 (非不透明) (C++11以降) 列挙子リスト を含む 列挙スコープ を導入します。

enum class E // 列挙型「E」の宣言
{            // 列挙スコープ「S」を導入
    // スコープ「S」はここから開始
    e1, e2, e3
    // スコープ「S」はここで終了
}

テンプレートパラメータスコープ

template template parameter は、 template parameter scope を導入します。このスコープは、そのテンプレートテンプレートパラメータのテンプレートパラメータリスト全体 および require (C++20以降) を含みます。

各テンプレート宣言 D は、 テンプレートパラメータスコープ S を導入します。このスコープは D のテンプレートパラメータリストの先頭から D の終わりまで拡張されます。テンプレートパラメータリストの外側にある宣言で S に属するはずのものは、代わりに D と同じスコープに属します。

テンプレートパラメータのみがテンプレートパラメータスコープに属し、テンプレートパラメータスコープのみがテンプレートパラメータスコープを親スコープとして持ちます。

// 「X」のクラステンプレート宣言は
// テンプレートパラメータスコープ「S1」を導入する
template
<
    // スコープ「S1」はここから始まる
    template // テンプレートテンプレートパラメータ「T」は
             // 別のテンプレートパラメータスコープ「S2」を導入する
    <
        typename T1
        typename T2
    > requires std::convertible_from<T1, T2> // スコープ「S2」はここで終了
    class T,
    typename U
>
class X; // スコープ「S1」はセミコロンの前で終了
namespace N
{
    template <typename T>
    using A = struct X; // 「X」はテンプレート宣言と同じスコープ、
                        // すなわち「N」のスコープに存在する
}

契約表明スコープ

契約表明 C は、 C を含む 契約表明スコープ を導入する。

もし 事後条件表明 識別子 を持ち、その識別子が 名前独立 ではなく、かつその事後条件表明が関数 func に関連付けられており、 潜在的に競合する 宣言 D のターゲットスコープが以下のスコープのいずれかである場合、プログラムは不適格となる:

  • func の関数パラメータスコープ。
  • D ラムダ式 に関連付けられている場合、事前条件表明の最も内側の外側のラムダスコープ。
(C++26以降)

宣言のポイント

一般的に、名前はその最初の宣言の locus の後に可視となり、これは以下のように位置づけられます。

単純宣言で宣言された名前のスコープは、その名前の declarator の直後、初期化子(もしあれば)の前に始まります。

int x = 32; // 外側のxがスコープ内にある
{
    int x = x; // 内側のxは初期化子 (= x) の前からスコープ内にある
               // これは外側のxの値(32)で内側のxを初期化するのではなく、
               // 内側のxを自身の(不定な)値で初期化する
}
std::function<int(int)> f = [&](int n){ return n > 1 ? n * f(n - 1) : n; };
// 関数fの名前はラムダ内でスコープ内にあり、
// 参照によって正しくキャプチャされ、再帰関数を実現する
const int x = 2; // 外側のxはスコープ内
{
    int x[x] = {}; // 内側のxは初期化子 (= {}) の前からスコープ内にあるが、
                   // 宣言子 (x[x]) の後から
                   // 宣言子内では、外側のxが依然としてスコープ内にある
                   // これは2つのintの配列を宣言する
}

クラスまたはクラステンプレート宣言の軌跡は、その template-id でテンプレート特殊化を指定するテンプレート)の class-head 内で、クラスを命名する識別子(または template-id の直後にあります。クラスまたはクラステンプレート名は基底クラスのリスト内ですでにスコープ内にあります。

struct S: std::enable_shared_from_this<S> {}; // Sはコロンの時点でスコープ内にある

enum specifier またはopaque enum宣言 (C++11以降) の位置は、列挙型を命名する識別子の直後です。

enum E : int // コロンの時点でEはスコープ内にある
{
    A = sizeof(E)
};

型エイリアスまたはエイリアステンプレートの宣言の locus は、エイリアスが参照する型IDの直後に位置します。

using T = int; // 外側のTはセミコロンの時点でスコープ内にある
{
    using T = T*; // 内側のTはセミコロンの時点でスコープ内にある
                  // 外側のTはセミコロンの前までスコープ内にある
                  // T = int* と同じ
}

using宣言におけるコンストラクタ以外を名前付けする宣言子のための locus は、宣言子の直後です。

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がスコープ内にある
          Base<next>::value; // Derived::valueは1
};

列挙子の位置は、その定義の直後です(変数の場合のように初期化子の前ではありません)。

const int x = 12;
{
    enum
    {
        x = x + 1, // 列挙子xはカンマの時点でスコープ内にあり、
                   // 外側のxはカンマの前までスコープ内にあり、
                   // 列挙子xは13で初期化される
        y = x + 1  // yは14で初期化される
    };
}

インジェクテッドクラス名の injected-class-name の位置は、そのクラス(またはクラステンプレート)定義の開きブレース直後です。

template<typename T>
struct Array
//  : std::enable_shared_from_this<Array> // エラー: 注入されたクラス名がスコープ内にない
    : std::enable_shared_from_this< Array<T> > // OK: テンプレート名Arrayがスコープ内にある
{ // 注入されたクラス名Arrayが公開メンバ名のようにスコープ内にある
    Array* p; // Array<T>へのポインタ
};

関数ローカルで事前定義された変数 __func__ の暗黙的な宣言の位置は、関数定義の関数本体の直前にあります。

(C++11以降)


構造化束縛宣言 のスコープは identifier-list の直後から始まるが、構造化束縛の初期化子は宣言されている名前のいずれも参照することが禁止されている。

(C++17以降)


range- for ループ range-declaration で宣言された変数 または構造化束縛 (C++17以降) のスコープは、 range-expression の直後に始まります。

std::vector<int> x;
for (auto x : x) // ベクトルxは閉じ括弧の前にスコープ内にあり、
                 // auto xは閉じ括弧の時点でスコープ内にある
{
    // auto xはスコープ内にある
}
(C++11以降)

テンプレートパラメータの軌跡は、その完全なテンプレートパラメータ(オプションのデフォルト引数を含む)の直後に位置します。

typedef unsigned char T;
template<
    class T = T, // コンマの時点でテンプレートパラメータTがスコープ内にあり、
                 // unsigned charのtypedef名はコンマの前にスコープ内にある
    T // テンプレートパラメータTがスコープ内にある
    N = 0
>
struct A
{
};

識別子 を持つ 事後条件表明 の位置は、その直後の : に置かれる。

(C++26以降)


concept定義 のスコープはconcept名の直後に始まりますが、concept定義では宣言されているconcept名自体を参照することは禁止されています。

(since C++20)

名前付き namespace definition の locus は、namespace 名の直後です。

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
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]

関連項目

**日本語訳:**
変更点: - "for" → "の" (所有・関連を示す助詞) - HTMLタグ、属性、C言語の専門用語("Scope")はそのまま保持