修飾名のルックアップ
修飾名とは、スコープ解決演算子 :: の右側に現れる名前です (「修飾識別子」も参照)。修飾名は以下を参照できます。
- クラスメンバ (静的および非静的関数、型、テンプレートなどを含む)
- 名前空間メンバ (他の名前空間を含む)
- 列挙子
:: の左側に何もなければ、探索はグローバル名前空間スコープでの宣言のみを考慮します。これにより、ローカルな宣言によって隠されている場合でも、そのような名前を参照することができます。
#include <iostream> namespace M { const char* fail = "fail\n"; } using M::fail; namespace N { const char* ok = "ok\n"; } using namespace N; int main() { struct std {}; std::cout << ::fail; // Error: unqualified lookup for 'std' finds the struct ::std::cout << ::ok; // OK: ::std finds the namespace std }
:: の右側の名前に対して名前探索を実行する前に、その左側の名前に対する探索を完了する必要があります (ただし、decltype 式が使用されている場合、または左側に何も記述されていない場合は除く)。この探索は、その名前の左側に別の :: があるかどうかによって、修飾または非修飾のいずれかになりますが、名前空間、クラス型、列挙型、および特殊化が型であるテンプレートのみを考慮します。左側に見つかった名前が名前空間、クラス、列挙型、または依存型を指定しない場合、プログラムは不適格です。
struct A { static int n; }; int main() { int A; A::n = 42; // OK: unqualified lookup of A to the left of :: ignores the variable A b; // Error: unqualified lookup of A finds the variable A } template<int> struct B : A {}; namespace N { template<int> void B(); int f() { return B<0>::n; // Error: N::B<0> is not a type } }
修飾名が宣言子として使用される場合、その修飾名の後に続く、同じ宣言子内で使用される名前 (ただし、先行する名前ではない) の非修飾探索は、そのメンバのクラスまたは名前空間のスコープで実行されます。
class X {}; constexpr int number = 100; struct C { class X {}; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // Error: look up for X finds ::X, not C::X C::X C::arr[number], brr[number]; // OK: size of arr is 50, size of brr is 100
:: の後に文字 ~ が続き、さらに識別子が続く場合 (つまり、デストラクタまたは疑似デストラクタを指定する場合)、その識別子は :: の左側の名前と同じスコープで探索されます。
struct C { typedef int I; }; typedef int I1, I2; extern int *p, *q; struct A { ~A(); }; typedef A AB; int main() { p->C::I::~I(); // The name I after ~ is looked up in the same scope as I before :: // (that is, within the scope of C, so it finds C::I) q->I1::~I2(); // The name I2 is looked up in the same scope as I1 // (that is, from the current scope, so it finds ::I2) AB x; x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before :: // (that is, from the current scope, so it finds ::AB) }
列挙子左側の名前の探索によって列挙型 (スコープ付きまたはスコープなし) が見つかった場合、右側の名前の探索はその列挙型に属する列挙子でなければなりません。そうでない場合、プログラムは不適格です。 |
(C++11以降) |
[編集] クラスメンバ
左側の名前の探索によってクラス/構造体名または共用体名が見つかった場合、:: の右側の名前はそのクラスのスコープで探索されます (したがって、そのクラスまたはその基底クラスのメンバの宣言を見つけることができます)。ただし、以下の例外があります。
- デストラクタは上記のように探索されます (
::の左側の名前のスコープで)。 - ユーザー定義変換関数の名前にある変換型識別子は、まずクラスのスコープで探索されます。見つからない場合、その名前は現在のスコープで探索されます。
- テンプレート引数で使用される名前は、現在のスコープで探索されます (テンプレート名のスコープではありません)。
- using-宣言内の名前も、同じスコープで宣言された変数、データメンバ、関数、または列挙子の名前によって隠されているクラス/列挙型名を考慮します。
| このセクションは未完成です 理由: 上記のマイクロ例 |
:: の右側が左側と同じクラスの名前である場合、その名前はそのクラスのコンストラクタを指定します。このような修飾名は、コンストラクタの宣言および継承コンストラクタのusing-宣言でのみ使用できます。関数名が無視される探索 (つまり、:: の左側の名前を探索する場合、明確化型指定子または基底指定子の名前を探索する場合) では、同じ構文が注入されたクラス名に解決されます。
struct A { A(); }; struct B : A { B(); }; A::A() {} // A::A names a constructor, used in a declaration B::B() {} // B::B names a constructor, used in a declaration B::A ba; // B::A names the type A (looked up in the scope of B) A::A a; // Error: A::A does not name a type struct A::A a2; // OK: lookup in elaborated type specifier ignores functions // so A::A simply names the class A as seen from within the scope of A // (that is, the injected-class-name)
修飾名探索は、ネストされた宣言または派生クラスによって隠されているクラスメンバにアクセスするために使用できます。修飾メンバ関数の呼び出しは決して仮想ではありません。
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // Calls D::foo (virtual dispatch) b.B::foo(); // Calls B::foo (static dispatch) }
[編集] 名前空間メンバ
:: の左側の名前が名前空間を参照する場合、または :: の左側に何も記述されていない場合 (この場合はグローバル名前空間を参照する)、:: の右側に現れる名前はその名前空間のスコープで探索されます。ただし、以下の例外があります。
- テンプレート引数で使用される名前は、現在のスコープで探索されます。
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // Error: X is looked up as ::X, not as N::X
名前空間 N のスコープ内の修飾探索は、まず N 内に存在するすべての宣言と N のインライン名前空間メンバ (および推移的に、それらのインライン名前空間メンバ) 内に存在するすべての宣言を考慮します。そのセットに宣言がない場合、N 内および N のすべての推移的なインライン名前空間メンバで見つかったusing-宣言によって指定されたすべての名前空間内の宣言を考慮します。ルールは再帰的に適用されます。
int x; namespace Y { void f(float); void h(int); } namespace Z { void h(double); } namespace A { using namespace Y; void f(int); void g(int); int i; } namespace B { using namespace Z; void f(char); int i; } namespace AB { using namespace A; using namespace B; void g(); } void h() { AB::g(); // AB is searched, AB::g found by lookup and is chosen AB::g(void) // (A and B are not searched) AB::f(1); // First, AB is searched. There is no f // Then, A, B are searched // A::f, B::f found by lookup // (but Y is not searched so Y::f is not considered) // Overload resolution picks A::f(int) AB::x++; // First, AB is searched. There is no x // Then A, B are searched. There is no x // Then Y and Z are searched. There is still no x: this is an error AB::i++; // AB is searched. There is no i // Then A, B are searched. A::i and B::i found by lookup: this is an error AB::h(16.8); // First, AB is searched. There is no h // Then A, B are searched. There is no h // Then Y and Z are searched // Lookup finds Y::h and Z::h. Overload resolution picks Z::h(double) }
同じ宣言が複数回見つかることは許可されています。
namespace A { int a; } namespace B { using namespace A; } namespace D { using A::a; } namespace BD { using namespace B; using namespace D; } void g() { BD::a++; // OK: finds the same A::a through B and through D }
| このセクションは未完成です 理由: N4861 6.5.3.2[namespace.qual] の残りの部分、彼らの例を短縮しようとする |
[編集] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 215 | C++98 | :: の前の名前はクラス名または名前空間でなければならない名前なので、テンプレートパラメータはそこでは許可されなかった。 |
その名前はクラス、 名前空間または依存型を指定しなければならない。 |
| CWG 318 | C++98 | :: の右側が左側と同じクラスの名前である場合、その修飾名は常にそのクラスのコンストラクタの名前であると 見なされていた。 |
コンストラクタの名前であるのは、 許容される場合のみ (例えば、 明確化型指定子ではない場合)。 |