Unqualified name lookup
修飾されていない名前、すなわちスコープ解決演算子
::
の右側に現れない名前については、名前探索は以下に説明する
スコープ
を調べ、何らかの宣言が少なくとも1つ見つかった時点で探索を停止し、それ以降のスコープは調査されません。(注:一部の文脈からの探索では特定の宣言がスキップされます。例えば、
::
の左側で使用される名前の探索では関数、変数、列挙子の宣言が無視され、基底クラス指定子として使用される名前の探索ではすべての非型宣言が無視されます)。
修飾されていない名前検索の目的において、 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; // 最初に見つかる } 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; { { // int i; // 見つからない namespace A { namespace N { // int i; // 見つからない { {
クラス定義
クラス定義内のどこかで使用される名前(基底クラス指定子やネストされたクラス定義を含む)について、メンバ関数本体の内部、メンバ関数のデフォルト引数、メンバ関数の例外指定、またはメンバ初期化子(そのメンバが外側のクラスの本体内で定義されているネストされたクラスに属する可能性がある場合)を除き、以下のスコープが検索されます:
friend 宣言の場合、それが以前に宣言されたエンティティを参照しているかどうかを判断するためのルックアップは、最も内側の外側の名前空間で停止することを除いて、上記と同様に進行します。
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; // 1番目に見つかる int a[i]; // iの使用 // static const int i = 1; // 見つからない }; // static const int i = 2; // 見つからない }; // const int i = 4; // 見つからない } // const int i = 5; // 見つからない
注入されたクラス名
クラスまたはクラステンプレートの定義内、またはそれらから派生したクラス内で使用されるクラス名またはクラステンプレート名については、非修飾名検索は、そのクラスが(公開メンバーアクセス権を持つ)メンバー宣言によって導入されたかのように、定義されているクラスを見つけます。詳細については、 injected-class-name を参照してください。
メンバー関数の定義
メンバ関数本体内部、メンバ関数のデフォルト引数、メンバ関数の例外仕様、またはデフォルトメンバ初期化子で使用される名前について、検索されるスコープは クラス定義 の場合と同じですが、名前を使用する宣言より前の部分だけでなく、クラス全体のスコープが考慮される点が異なります。ネストされたクラスの場合、外側のクラスの本体全体が検索されます。
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; // 1番目に見つかる i = 16; // int i; // 見つからない } namespace M { namespace N { // int i; // 見つからない } }
- いずれの場合でも、基底クラスを調べる際には、以下の規則(時に 仮想継承における優位性 と呼ばれる)が適用されます:
| Bの部分オブジェクトで見つかったメンバー名は、AがBの基底クラス部分オブジェクトである場合、任意の部分オブジェクトA内の同じメンバー名を隠蔽する。(これは、Bの基底ではない継承階層内のAの追加の非仮想コピーでは名前を隠蔽しないことに注意:この規則は仮想継承にのみ影響する。)using宣言によって導入された名前は、宣言を含むクラス内の名前として扱われる。各基底を検査した後、結果のセットには、同じ型の部分オブジェクトからの静的メンバーの宣言、または同じ部分オブジェクトからの非静的メンバーの宣言のいずれかが含まれなければならない。 | (until C++11) |
宣言とそれらの宣言が見つかった部分オブジェクトから構成される
ルックアップセット
が構築される。using宣言はそれらが表すメンバーで置き換えられ、型宣言(注入されたクラス名を含む)はそれらが表す型で置き換えられる。名前が使用されたスコープのクラスがCである場合、Cが最初に検査される。C内の宣言リストが空の場合、その直接基底Biごとにルックアップセットが構築される(Biが独自の基底クラスを持つ場合はこれらの規則を再帰的に適用する)。構築されると、直接基底のルックアップセットは以下のようにCのルックアップセットにマージされる:
|
(since 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を隠蔽するため、DからB2経由でX::fに到達可能であっても、 // 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内のX::fが*置換*される。なぜならCに既に追加されたすべての部分オブジェクト(X)が // 新しいセット(B1)内の少なくとも1つの部分オブジェクトの基底クラスとなるため。 // 最終結果は同じ:Cの検索セットはB1内で見つかったB1::fのみを保持する)
-
Bの静的メンバー、Bのネストされた型、およびBで宣言された列挙子を検索する非修飾名探索は、 調査対象のクラスの継承ツリーに型Bの非仮想基本クラス部分オブジェクトが複数存在する場合でも曖昧さが生じない:
struct V { int v; }; struct B { int a; static int s; enum { e }; }; struct B1 : B, virtual V {}; struct B2 : B, virtual V {}; struct D : B1, B2 {}; void f(D& pd) { ++pd.v; // OK: 仮想基底クラスのサブオブジェクトが1つだけなので、vは1つだけ ++pd.s; // OK: B1とB2の両方で見つかるが、staticなB::sは1つだけ int i = pd.e; // OK: B1とB2の両方で見つかるが、列挙子B::eは1つだけ ++pd.a; // エラー、曖昧: B1のB::aとB2のB::a }
フレンド関数の定義
クラス内でフレンドシップを許可しているクラスの本体内部で定義された friend 関数で使用される名前については、非修飾名前探索はメンバ関数と同じ方法で行われます。クラスの本体外部で定義された friend 関数で使用される名前については、非修飾名前探索は名前空間内の関数と同じ方法で行われます。
int i = 3; // f1では3番目、f2では2番目に見つかる struct X { static const int i = 2; // f1では2番目に見つかる、f2では見つからない friend void f1(int x) { // int i; // 1番目に見つかる i = x; // X::iを見つけて変更する } friend int f2(); // static const int i = 2; // クラススコープ内のどこでもf1に対して2番目に見つかる }; void f2(int x) { // int i; // 1番目に見つかる i = x; // ::iを見つけて変更する }
フレンド関数宣言
別のクラスからのメンバ関数をフレンド指定する friend 関数宣言の宣言子で使用される名前について、その名前が 宣言子 識別子内のテンプレート引数の一部でない場合、非修飾名前検索はまずメンバ関数のクラスのスコープ全体を調べます。そのスコープで見つからない場合(または名前が宣言子識別子内のテンプレート引数の一部である場合)、検索はフレンド指定を行っているクラスのメンバ関数であるかのように続行されます。
template<class T> struct S; // メンバ関数がフレンド指定されるクラス struct A { typedef int AT; void f1(AT); void f2(float); template<class T> void f3(); void f4(S<AT>); }; // f1、f2、f3に対してフレンド指定を許可するクラス struct B { typedef char AT; typedef float BT; friend void A::f1(AT); // ATのルックアップはA::ATを発見(ATはA内で発見) friend void A::f2(BT); // BTのルックアップはB::BTを発見(BTはA内で発見されず) friend void A::f3<AT>(); // ATのルックアップはB::ATを発見(A内でのルックアップは行われない、 // ATは宣言子識別子A::f3<AT>内にあるため) }; // f4に対してフレンド指定を許可するクラステンプレート template<class AT> struct C { friend void A::f4(S<AT>); // ATのルックアップはA::ATを発見 // (ATは宣言子識別子A::f4内にないため) };
デフォルト引数
関数宣言における デフォルト引数 で使用される名前、またはコンストラクタの メンバー初期化子 の 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 ではない // また、パラメータはデフォルト引数として許可されていない
静的データメンバーの定義
static data member の定義で使用される名前については、メンバー関数の定義で使用される名前と同様の方法で名前探索が行われます。
struct X { static int x; static const int n = 1; // 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はcolor::REDを見つけ、::REDではないため、GREEN = 2 BLUE = ::RED + 4 // 修飾付きルックアップは::REDを見つけ、BLUE = 11 };
関数 try ブロックのハンドラ
関数の ハンドラ で使用される名前について、 関数 try ブロック の場合は、関数本体の最も外側のブロックの先頭で使用された名前であるかのように名前探索が行われます(特に、関数パラメータは可視ですが、その最も外側のブロックで宣言された名前は可視ではありません)
int n = 3; // 3番目に見つかる int f(int n = 2) // 2番目に見つかる try { int n = -1; // 見つからない } catch(...) { // int n = 1; // 1番目に見つかる assert(n == 2); // nの検索で関数パラメータfが見つかる throw; }
オーバーロードされた演算子
式で使用される operator (例: operator + が a + b で使用される場合)について、その名前探索ルールは operator + ( a, b ) のような明示的な関数呼び出し式で使用されるoperatorとは若干異なります:式を解析する際、非メンバーoperatorオーバーロードとメンバーoperatorオーバーロード(両方の形式が許可されている演算子について)の2つの独立した探索が実行されます。これらの集合は、組み込みoperatorオーバーロードと overload resolution で説明されている通り同等の立場で統合されます。明示的な関数呼び出し構文が使用される場合は、通常の非修飾名前探索が実行されます:
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+を発見、非メンバー探索で // ::operator+(A, A)を発見、オーバーロード解決で ::operator+(A, A)を選択 }
テンプレート定義
テンプレート定義内で使用される non-dependent name については、テンプレート定義が解析される時点で非修飾名探索が行われます。この時点でなされた宣言への束縛は、インスタンス化時点で可視な宣言の影響を受けません。テンプレート定義内で使用される dependent name については、探索はテンプレート引数が判明するまで延期され、その時点で ADL はテンプレート定義コンテキストから可視な関数宣言とテンプレートインスタンス化コンテキストから可視な関数宣言の両方を調査します (外部リンケージを持つ) (C++11まで) 。一方、非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)を、ADLにより ::f(E)を見つけ // その後オーバーロード解決が ::f(E)を選択 // これはf(char)を1回、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のルックアップは ::A(double)を見つけ、B<T>::Aではない };
注: この規則の根拠と影響については dependent name lookup rules を参照してください。
テンプレート名
|
この節は不完全です
理由: -> および . の後のテンプレート名のデュアルスコープ検索 |
クラステンプレートのメンバーのテンプレート外定義
| このセクションは不完全です |
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 490 | C++98 |
フレンドメンバ関数宣言内のテンプレート引数における
全ての名前は、メンバ関数のクラススコープで 検索されなかった |
宣言子識別子内のテンプレート引数の
名前のみを除外する |
| CWG 514 | C++98 |
名前空間スコープで使用される全ての非修飾名は
最初にそのスコープで検索された |
名前空間外で定義される名前空間変数メンバーの
定義に使用される非修飾名は、最初に その名前空間内で検索される |
参考文献
- C++23標準 (ISO/IEC 14882:2024):
-
- 6.5 名前探索 [basic.lookup] (p: 44-45)
-
- 6.5.2 メンバー名探索 [class.member.lookup] (p: 45-47)
-
- 13.8 名前解決 [temp.res] (p: 399-403)
- C++20標準 (ISO/IEC 14882:2020):
-
- 6.5 名前探索 [basic.lookup] (p: 38-50)
-
- 11.8 メンバー名探索 [class.member.lookup] (p: 283-285)
-
- 13.8 名前解決 [temp.res] (p: 385-400)
- C++17標準 (ISO/IEC 14882:2017):
-
- 6.4 名前探索 [basic.lookup] (p: 50-63)
-
- 13.2 メンバー名探索 [class.member.lookup] (p: 259-262)
-
- 17.6 名前解決 [temp.res] (p: 375-378)
- C++14標準 (ISO/IEC 14882:2014):
-
- 3.4 名前探索 [basic.lookup] (p: 42-56)
-
- 10.2 メンバー名探索 [class.member.lookup] (p: 233-236)
-
- 14.6 名前解決 [temp.res] (p: 346-359)
- C++11標準 (ISO/IEC 14882:2011):
-
- 3.4 名前探索 [basic.lookup]
-
- 10.2 メンバー名探索 [class.member.lookup]
-
- 14.6 名前解決 [temp.res]
- C++98標準 (ISO/IEC 14882:1998):
-
- 3.4 名前探索 [basic.lookup]
-
- 10.2 メンバー名探索 [class.member.lookup]
-
- 14.6 名前解決 [temp.res]