名前空間
変種
操作

非修飾名の名前探索

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (C++20未満)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

非修飾名 (スコープ解決演算子 :: の右に現れるのでない名前) の場合、名前探索は、何らかの種類の宣言が少なくともひとつ見つかるまで、下で述べるように、スコープを調べます。 見つかった時点で探索は停止し、それ以上のスコープは調べられません (ノート: 一部の文脈における名前探索は一部の宣言をスキップします。 例えば、 :: の左に使用された名前の名前探索は関数、変数、および列挙子の宣言を無視し、基底クラス指定子として使用された名前の名前探索はすべての非型宣言を無視します)。

非修飾の名前探索の目的のために、 using 指令によって指定された名前空間内のすべての宣言は、その using 指令および指定された名前空間の両方を直接または間接的に含む最も近い囲っている名前空間で宣言されたかのように現れます。

関数呼び出し演算子 (および式内の演算子) の左に使用された名前の非修飾名の名前探索は実引数依存の名前探索で説明しています。

目次

[編集] ファイルスコープ

グローバル (トップレベル名前空間) スコープ (関数、クラス、またはユーザ宣言名前空間の外側) で使用された名前の場合は、その名前の使用より前のグローバルスコープが調べられます。

int n = 1;     // n の宣言。
int x = n + 1; // OK、 ::n を発見します。
 
int z = y - 1; // エラー、名前探索は失敗します。
int y = 2;     // y の宣言。

[編集] 名前空間スコープ

ユーザ宣言名前空間内かつ関数またはクラスの外側で使用された名前の場合は、その名前空間のその名前の使用より前が検索され、その後、その名前空間を囲っている名前空間のその名前空間の宣言より前が検索され、グローバル名前空間に達するまで同様に繰り返されます。

int n = 1; // 宣言。
namespace N {
  int m = 2;
  namespace Y {
    int x = n; // OK、 ::n を発見します。
    int y = m; // OK、 ::N::m を発見します。
    int z = k; // エラー、名前探索は失敗します。
  }
  int k = 3;
}

[編集] 名前空間の外側での定義

名前空間の外側での名前空間メンバの変数の定義で使用された名前の場合、名前空間はその名前空間の内側で使用された名前に対するものと同じ方法で行われます。

namespace X {
    extern int x; // 宣言。 定義ではありません。
    int n = 1; // 1番目に発見されます。
};
int n = 2; // 2番目に発見されます。
int X::x = n; // X::n を発見し、 X::x を 1 に設定します。

[編集] 非メンバ関数の定義

ユーザ宣言またはグローバル名前空間のメンバである関数の定義 (その本体またはデフォルト引数の部分) で使用された名前の場合は、その名前が使用されたブロックのその名前の使用より前が検索され、その後、囲っているブロックのそのブロックの開始点より前が検索され、その関数の本体であるブロックに到達するまで同様に繰り返されます。 その後、その関数が宣言された名前空間のその名前を使用した関数の定義 (宣言であるとは限りません) より前が検索され、その後、囲っている名前空間が検索され、以下同様です。

namespace A {
   namespace N {
       void f();
       int i=3; // 3番目に発見されます (2番目が存在しない場合)。
    }
    int i=4; // 4番目に発見されます (3番目が存在しない場合)。
}
 
int i=5; // 5番目に発見されます (4番目が存在しない場合)。
 
void A::N::f() {
    int i = 2; // 2番目に発見されます (1番目が存在しない場合)。
    while(true) {
       int i = 1; // 1番目に発見されます。 名前探索はここで終わります。
       std::cout << i; // i の使用。
    }
}
 
// int i; // 発見されません。
 
namespace A {
  namespace N {
    // int i; // 発見されません。
  }
}

[編集] クラス定義

クラス定義内の任意の場所 (メンバ関数の本体、メンバ関数のデフォルト引数、メンバ関数の例外指定、デフォルトメンバ初期化子、およびネストしたクラスの定義 (そのネストしたクラスの基底の名前を含みます) は除きます) で使用された名前の場合は、以下のスコープが検索されます。

a) そのクラスの本体の、その名前が使用された地点まで。
b) その基底クラスの本体全体 (何も宣言が発見されないときは、さらにその基底に再帰します)。
c) このクラスがネストしている場合は、その囲っているクラスのこのクラスの宣言の地点までと、囲っているクラスの基底クラスの本体全体。
d) このクラスがローカルなクラスまたはローカルなクラス内にネストしている場合は、そのクラスが定義されているブロックスコープのそのクラスの定義の地点まで。
e) このクラスが名前空間のメンバ、または名前空間のメンバであるクラス内にネストしている、または名前空間のメンバである関数内のローカルなクラスの場合は、その名前空間のスコープが、そのクラス、囲っているクラス、または関数の定義の地点まで、検索されます。 フレンド宣言によって導入された名前に対する名前探索の場合は、最も内側の囲っているクラスのみが考慮されます。 そうでなければ、通常通りにグローバルスコープまで、囲っている名前空間の探索が続けられます。
namespace M {
    // const int i = 1; // 発見されません。
    class B {
        // static const int i = 3; // 3番目に発見されます (が、その後のアクセスチェックによって拒否されます)。
    };
}
// const int i = 5; // 5番目に発見されます。
namespace N {
    // const int i = 4; // 4番目に発見されます。
    class Y : public M::B {
        // static const int i = 2; // 2番目に発見されます。
        class X {
            // static const int i = 1; // 最初に発見されます。
            int a[i]; // i の使用。
            // static const int i = 1; // 発見されません。
        };
        // static const int i = 2; // 発見されません。
    };
    // const int i = 4; // 発見されません。
}
// const int i = 5; // 発見されません。

[編集] 注入されたクラス名

クラスまたはクラステンプレートまたは派生クラスで使用された、そのクラスまたはテンプレートの名前の場合は、非修飾名の名前探索において、その名前が (パブリックメンバアクセスを持つ) メンバ宣言によって導入されたかのように、定義中のクラスが発見されます。 さらなる詳細については注入されたクラス名を参照してください。

[編集] メンバ関数の定義

メンバ関数の本体、メンバ関数のデフォルト引数、メンバ関数の例外指定、デフォルトメンバ初期化子 の内側で使用された名前の場合、またはネストしたクラス定義内 (そのネストしたクラスの基底クラスの名前を含みます) で使用された名前の場合は、検索されるスコープはクラス定義の場合と同じですが、その名前が使用された宣言より前の部分だけでなく、そのクラスのスコープ全体が考慮されます。 ネストしたクラスの場合は、囲っているクラスの本体全体が検索されます。

class B {
    // int i; // 3番目に発見されます。
};
namespace M {
    // int i; // 5番目に発見されます。
    namespace N {
        // int i; // 4番目に発見されます。
        class X : public B {
            // int i; // 2番目に発見されます。
            void f();
            // int i; // 同じく2番目に発見されます。
        };
        // int i; // 4番目に発見されます。
    }
}
// int i; // 6番目に発見されます。
void M::N::X::f()
{
    // int i; // 最初に発見されます。
    i = 16; // i の使用。
    // int i; // 発見されません。
}
namespace M {
  namespace N {
    // int i; // 発見されません。
  }
}
いずれの場合も、クラスの基底が調べられるときは、しばしば仮想継承における優勢と呼ばれる、以下のルールに従います。
AB の基底クラス部分オブジェクトである場合、部分オブジェクト B 内で発見されるメンバ名はあらゆる部分オブジェクト A 内の同じメンバ名を隠蔽します (これは B の基底でない継承階層上の追加の非仮想な A のコピー内の名前は隠蔽しないことに注意してください。 このルールは仮想継承に対してのみ効果を持ちます)。 using 宣言によって導入された名前は、その宣言を含むクラス内の名前として扱われます。 各基底を調べた後、結果の集合は、同じ型の部分オブジェクトの静的メンバの宣言か、同じ部分オブジェクトの非静的メンバの宣言の、いずれかを含まなければなりません。 (C++11未満)
宣言およびそれらの宣言が発見された部分オブジェクトから構成される、名前探索集合が構築されます。 using 宣言はそれが表すメンバに置き換えられ、型宣言 (注入されたクラス名を含みます) はそれが表す型に置き換えられます。 C が、名前が使用されたスコープを持つクラスである場合は、まず C が調べられます。 C 内の宣言のリストが空の場合は、名前探索集合はその直接の基底 Bi のそれぞれに対して構築されます (Bi がそれ自身の基底を持つ場合は、これらのルールが再帰的に適用されます)。 構築後、直接の基底に対する名前探索集合が、以下のように、 C の名前探索集合にマージされます。
  • Bi 内の宣言の集合が空の場合は、それは破棄されます。
  • 構築された C の名前探索集合がその時点で空である場合は、それは Bi の名前探索集合で置き換えられます。
  • Bi の名前探索集合内のすべての部分オブジェクトが、 C の名前探索集合にすでに追加された部分オブジェクトの少なくともひとつの基底である場合は、 Bi の名前探索集合は破棄されます。
  • C の名前探索集合にすでに追加されたすべての部分オブジェクトが、 Bi の名前探索集合内の部分オブジェクトの少なくともひとつの基底である場合は、 C の名前探索集合は破棄され、 Bi の名前探索集合で置き換えられます。
  • そうでなく、 Bi 内の宣言集合と C 内の宣言集合が異なる場合は、結果は曖昧なマージです。 C の新しい名前探索集合は無効な宣言を持ち、以前に C にマージされた部分オブジェクトと Bi から導入された部分オブジェクトの和集合になります。 この無効な名前探索集合は、後に破棄される場合は、エラーではありません。
  • そうでなければ、 C の新しい名前探索集合は共有された宣言集合を持ち、以前に C にマージされた部分オブジェクトと Bi から導入された部分オブジェクトの和集合になります。
(C++11以上)
struct X { void f(); };
struct B1: virtual X { void f(); };
struct B2: virtual X {};
struct D : B1, B2 {
    void foo() {
        X::f(); // OK、 X::f を呼びます (修飾名の名前探索)。
        f(); // OK、 B1::f を呼びます (非修飾名の名前探索)。
// C++98 のルール: B1::f は X::f を隠蔽するため、たとえ X::f が B2 を通して
// D から到達可能であっても、 D からの名前探索では発見されません。
// C++11 のルール: D における f に対する名前探索集合は何も発見せず、基底を見に行きます。
//   B1 の f に対する名前探索集合は B1::f を発見し、そこで終わります。
// マージによって空の集合が置き換えられ、 C の f に対する名前探索集合は B1 内の B1::f を持ちます。
//   B2 の f に対する名前探索集合は何も発見せず、基底を見に行きます。
//     X の f に対する名前探索集合は X::f を発見します。
//   マージによって空の集合が置き換えられ、 B2 の f に対する名前探索集合は X 内の X::f を持ちます。
// C へのマージによって、 B2 における名前探索集合内のすべての部分オブジェクト (X) は
// すでにマージされたすべての部分オブジェクト (B1) の基底であるため、 B2 の集合は破棄されます。
// C は B1 で発見された B1::f のみを持って残されます。
// (もし struct D : B2, B1 であったならば、 C にすでに追加されたすべての部分オブジェクト (X) は
// 新しい集合内の部分オブジェクトの少なくともひとつ (B1) の基底であったでしょうから、
// 最後のマージは C のそれまでマージされた X 内の X::f を置き換えたでしょう。
// 最終結果は同じです。 C の名前探索集合は B1 で発見された B1::f のみを保持します)
    }
};
B の静的メンバ、 B のネストした型、および B で宣言された列挙子を発見する非修飾名の名前探索は、たとえ調べ中のクラスの継承ツリーに型 B の非仮想基底部分オブジェクトが複数あっても、曖昧ではありません。
struct V { int v; };
struct A {
        int a;
        static int s;
        enum { e };
};
struct B : A, virtual V { };
struct C : A, virtual V { };
struct D : B, C { };
 
void f(D& pd) {
        ++pd.v; // OK、唯一の仮想基底部分オブジェクトであるため、 v はひとつだけです。
        ++pd.s; // OK、たとえ B 経由と C 経由の両方で発見されても、静的な A::s はひとつだけです。
        int i = pd.e; // OK、たとえ B 経由と C 経由の両方で発見されても、列挙子 A::e はひとつだけです。
        ++pd.a; // エラー、 B 内の A::a と C 内の A::a の間で曖昧です。
}

[編集] フレンド関数の定義

フレンド関係が認められたクラスの本体の内側で定義されたフレンド関数の定義内で使用された名前の場合は、非修飾名の名前探索はメンバ関数の場合と同じ方法で行われます。 クラスの本体の外側で定義されたフレンド関数内で使用された名前の場合は、非修飾名の名前探索は名前空間内のクラスの場合と同じ方法で行われます。

int i = 3; // f に対しては3番目、 f2 に対しては2番目に発見されます。
struct X {
    static const int i = 2; // f1 に対しては2番目に発見され、 f2 に対しては発見されません。
    friend void f1(int x)
    {
        // int i; // 最初に発見されます。
        i = x; // X::i を発見します。
    }
    friend int f2();
    // static const int i = 2; // クラススコープ内のどこでも、 f1 に対して2番目に発見されます。
};
void f2(int x) {
    // int i; // 最初に発見されます。
    i = x; // ::i を発見します。
}

[編集] フレンド関数の宣言

別のクラスのメンバ関数をフレンドとするフレンド関数宣言の宣言子で使用された名前の場合は、その名前がテンプレートの実引数の一部でなければ、非修飾名の名前探索は、まずそのメンバ関数のクラスのスコープ全体を調べます。 そのスコープで見付からなければ (または名前がテンプレートの実引数の一部である場合は)、フレンド関係が認められたクラスのメンバ関数であるかのように名前探索が続けられます。

// フレンド関係が認められたメンバ関数を持つクラス。
struct A { 
    typedef int AT;
    void f1(AT);
    void f2(float);
    template <class T> void f3();
};
 
// フレンド関係を認めるクラス。
struct B {
    typedef char AT;
    typedef float BT;
    friend void A::f1(AT); // AT に対する名前探索は A::AT を発見します。
    friend void A::f2(BT); // BT に対する名前探索は B::BT を発見します。 
    friend void A::f3<AT>(); // AT に対する名前探索は B::AT を発見します。
};

[編集] デフォルト引数

関数宣言のデフォルト引数で使用された名前、またはコンストラクタのメンバ初期化子expression の部分で使用された名前の場合は、囲っているブロック、クラス、または名前空間のスコープが調べられる前に、まずその関数の仮引数が探されます。

class X {
    int a, b, i, j;
public:
    const int& r;
    X(int i): r(a), // X::r を X::a を参照するように初期化します。
              b(i), // X::b を引数 i の値に初期化します。
              i(i), // X::i を引数 i の値に初期化します。
              j(this->i) // X::j を X::i の値に初期化します。
    { }
}
 
int a;
int f(int a, int b = a); // エラー。 a に対する名前探索は ::a ではなく引数 a を発見しますが、
                         // デフォルト引数に引数を使用することはできません。

[編集] 静的データメンバの定義

静的データメンバの定義で使用された名前の場合は、名前探索はメンバ関数の定義で使用された名前の場合と同い方法で行われます。

struct X {
    static int x;
    static const int n = 1; // 最初に発見されます。
};
int n = 2; // 2番目に発見されます。
int X::x = n; // X::n を発見します。 X::x を 2 ではなく 1 に設定します。

[編集] 列挙子の宣言

列挙子の宣言の初期化子の部分で使用された名前の場合は、非修飾名の名前探索は、囲っているブロック、クラス、または名前空間のスコープを調べる前に、まず同じ列挙内のそれまでに宣言された列挙子が探されます。

const int RED = 7;
enum class color {
    RED,
    GREEN = RED+2, // RED は ::RED ではなく color::RED を発見するため、 GREEN = 2 です。
    BLUE = ::RED+4 // 修飾名の名前探索は ::RED を発見するため、 BLUE = 11 です。
};

[編集] 関数 try ブロックの catch 節

関数 try ブロックの catch 節で使用された名前の場合は、名前探索はその関数本体の最も外側のブロックの最も先頭で使用された名前であるかのように行われます (特に、関数の引数は可視ですが、最も外側のブロックで宣言された名前は不可視です)。

int n = 3; // 3番目に発見されます。
int f(int n = 2) // 2番目に発見されます。
try {
   int n = -1;  // 発見されません。
} catch(...) {
   // int n = 1; // 最初に発見されます。
   assert(n == 2); // n に対する名前探索は関数の引数の n を発見します。
   throw;
}

[編集] オーバーロードされた演算子

式で使用された演算子 (a+b で使用された operator+ など) の場合、名前探索のルールは明示的な関数呼び出し式で使用された演算子 (operator+(a,b) など) の場合と若干異なります。 式を解析するときは、異なる2つの名前探索、非メンバ演算子オーバーロードに対する名前探索とメンバ演算子オーバーロードに対する名前探索が行われます (両方の形式が許される演算子の場合)。 その後、これらの集合はがオーバーロード解決で説明されているように、組み込みの演算子と共に、平等にマージされます。 明示的な関数呼び出しの構文が使用された場合は、通常の非修飾名の名前探索が行われます。

struct A {};
void operator+(A, A); // ユーザ定義の非メンバ operator+。
 
struct B {
    void operator+(B); // ユーザ定義のメンバ operator+。
    void f ();
};
 
A a;
 
void B::f() // B のメンバ関数の定義。
{
    operator+(a,a); // エラー、メンバ関数内からの通常の名前探索は
                    // B のスコープで operator+ の宣言を発見し、そこで終わります。
                    // グローバルスコープに到達することはありません。
    a + a; // OK、メンバの名前探索が B::operator+(B) を発見し、非メンバの名前探索が
           // ::operator+(A,A) を発見し、オーバーロード解決が ::operator+(A,A) を選択します。
}

[編集] テンプレートの定義

テンプレートの定義で使用された非依存名の場合は、非修飾名の名前探索は、そのテンプレートの定義が調べられたときに行われます。 その時点で行われている宣言への束縛は実体化時点の可視な宣言には影響されません。 テンプレートの定義で使用された依存名の場合は、名前探索はテンプレートの実引数が判明するまで遅延されます。 これはテンプレートの定義の文脈からおよびテンプレートの実体化の文脈で可視な外部リンケージを持つ (C++11未満)関数宣言を ADL が調べるときです。 それに対して非 ADL の名前探索はテンプレートの定義の文脈から可視な外部リンケージを持つ (C++11未満)関数の宣言を調べるだけです (別の言い方をすると、テンプレートの定義の後に新しい関数の宣言を追加しても、 ADL を通した場合を除いて、可視になりません)。 何らかの別の翻訳単位に宣言された、 ADL の名前探索によって調べられる名前空間内の、外部リンケージを持つ、より良いマッチが存在する場合、または、もしそれらの翻訳単位が調べられたならば名前探索が曖昧になったであろう場合は、動作は未定義です。 いかなる場合でも、基底クラスがテンプレート引数に依存する場合は、そのスコープは非修飾名の名前探索では調べられません (定義の時点でも実体化の時点でも)。

void f(char); // f の最初の宣言。
 
template<class T> 
void g(T t) {
    f(1);    // 非依存名。 名前探索は ::f(char) を発見し、それを直ちに束縛します。
    f(T(1)); // 依存名。 名前探索は遅延されます。
    f(t);    // 依存名。 名前探索は遅延されます。
//  dd++;    // 非依存名。 名前探索は宣言を発見しません。
}
 
enum E { e };
void f(E);   // f の2番目の宣言。
void f(int); // f の3番目の宣言。
double dd;
 
void h() {
    g(e);  // g<E> を実体化します。
           // この時点で名前「f」の2番目と3番目の使用が名前探索され、
           // ::f(char) (名前探索によって) と ::f(E) (ADL によって) が発見され、
           // オーバーロード解決が ::f(E) を選択します。
           // この呼び出しは f(char) を呼んだ後 f(E) を2回呼びます。
    g(32); // g<int> を実体化します。
           // この時点で名前「f」の2番目と3番目の使用が名前探索され、
           // ::f(char) だけが発見され、
           // オーバーロード解決が ::f(char) を選択します。
           // この呼び出しは f(char) を3回呼びます。
}
 
typedef double A;
template<class T> class B {
   typedef int A;
};
template<class T> struct X : B<T> {
   A a; // A に対する名前探索は B<T>::A ではなく ::A (すなわち double) を発見します。
};

ノート: このルールの論拠および影響については依存名の名前探索ルールを参照してください。

[編集] テンプレート名

[編集] テンプレートの外側でのクラステンプレートのメンバ

[編集] 参考文献

  • C++11 standard (ISO/IEC 14882:2011):
  • 3.4 Name lookup [basic.lookup]
  • 10.2 Member name lookup [class.member.lookup]
  • 14.6 Name resolution [temp.res]
  • C++98 standard (ISO/IEC 14882:1998):
  • 3.4 Name lookup [basic.lookup]
  • 10.2 Member name lookup [class.member.lookup]
  • 14.6 Name resolution [temp.res]

[編集] 関連項目