名前空間
変種
操作

修飾名の名前探索

提供: 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 宣言によってグローバル名前空間に導入された) 宣言のみを考慮します。 これはたとえ名前がローカル宣言によって隠蔽されていてもそのような名前を参照することを可能とします。

#include <iostream>
int main() {
  struct std{};
  std::cout << "fail\n"; // エラー、非修飾名 std の名前探索は構造体を発見します。
  ::std::cout << "ok\n"; // OK、::std は名前空間 std を発見します。
}

:: の右側の名前に対して名前探索が行われる前に、左側の名前に対して名前探索が完了しなければなりません (decltype 式が使用された場合または左側に何もない場合は除きます)。 この名前探索 (修飾名の場合も非修飾名の場合もあります (その名前の左に別の :: があるかどうかによります)) は、名前空間、クラス型、列挙、および特殊化が型であるテンプレートのみを考慮します。

struct A {
  static int n;
};
int main() {
  int A;
  A::n = 42;    // OK、 :: の左の非修飾名 A の名前探索は変数を無視します。
  A b;          // エラー、非修飾名 A の名前探索は変数 A を発見します。
}

修飾名が宣言子として使用された場合は、同じ宣言子内で使用された、その修飾名の後の名前 (前の名前には適用されません) の非修飾名の名前探索は、そのメンバのクラスまたは名前空間のスコープで行われます。

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];    // エラー、 X に対する名前探索は C::X ではなく ::X を発見します。
C::X C::arr[number], brr[number]; // OK、 arr のサイズは 50、 brr のサイズは 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(); // ~ の後の名前 I は :: の前の I と同じスコープで、つまり
                 // C のスコープで名前探索されるため、 C::I を発見します。
  q->I1::~I2();  // 名前 I2 は I1 と同じスコープで、つまり現在のスコープで
                 // 名前探索されるため、 ::I2 を発見します。
  AB x;
  x.AB::~AB();   // ~ の後の名前 AB は :: の前の AB と同じスコープで、つまり
                 // 現在のスコープで名前探索されるため、 ::AB を発見します。
}

目次

列挙子

左側の名前探索の結果が列挙子 (スコープ付きまたはスコープなしのいずれか) の場合、右側の名前探索の結果はその列挙に属する列挙子でなければならず、そうでなければプログラムは ill-formed です。

(C++11以上)

[編集] クラスメンバ

左側の名前探索の結果がクラス/構造体または共用体の名前の場合、 :: の右側の名前はそのクラスのスコープで名前探索されます (そのためそのクラスまたはその基底のメンバの宣言を発見できます) が、以下の例外があります。

  • デストラクタは上で述べたように (:: の左の名前のスコープで) 名前探索されます。
  • ユーザ定義変換関数名の conversion-type-id は、まずそのクラスのスコープで名前探索されます。 見つからなければ、現在のスコープで名前探索されます。
  • テンプレートの実引数で使用される名前は、現在のスコープ (テンプレート名のスコープではなく) で名前探索されます。
  • using 宣言内の名前は、同じスコープで宣言された変数、データメンバ、または列挙子の名前によって隠蔽されたクラス/列挙の名前も考慮します。

:: の右側が左側と同じクラスを表す場合、その名前はそのクラスのコンストラクタを指定します。 そのような修飾名は、コンストラクタの宣言および継承コンストラクタに対する using 宣言でのみ使用できます。 関数名が無視されるそれらの名前探索では (つまり、 :: の左の名前に対して名前探索するとき、または複雑型指定子内または基底指定子内の名前を名前探索するとき)、その構文は注入されたクラス名に解決されます。

struct A { A(); };
struct B : A { B(); };
A::A() { } // A::A はコンストラクタを表します (宣言で使用されています)。
B::B() { } // B::B はコンストラクタを表します (宣言で使用されています)。
B::A ba;   // B::A は型 A を表します (B のスコープで名前探索されます)
A::A a;    // エラー、 A::A は型を表しません。
 
struct A::A a2; // OK、複雑型指定子内の名前探索は関数を無視するため、
                // A::A は A のスコープ内から見ているかのように
                // クラス A (つまり注入されたクラス名) を表します。

修飾名の名前探索は、ネストした宣言または派生クラスによって隠されたクラスメンバにアクセスするために使用できます。 修飾名のメンバ関数の呼び出しは仮想になることはありません。

struct B { virtual void foo(); };
struct D : B { void foo() override; };
int main()
{
    D x;
    B& b = x;
    b.foo(); // D::foo を呼びます (仮想ディスパッチ)。
    b.B::foo(); // B::foo を呼びます (静的ディスパッチ)。
}

[編集] 名前空間のメンバ

:: の左の名前が名前空間を参照する場合、または :: の左に何もない場合 (その場合はグローバル名前空間を参照します)、 :: の右側に現れる名前はその名前空間のスコープで探索されます。 ただし、

  • テンプレートの実引数で使用された名前は、現在のスコープで名前探索されます。
namespace N {
   template<typename T> struct foo {};
   struct X {};
}
N::foo<X> x; // エラー、 X は N::X ではなく ::X として名前探索されます。

名前空間 N のスコープ内の修飾名の名前探索は、まず N 内に位置するすべての宣言を、そして Nインライン名前空間メンバ内 (そして、推移的に、それらのインライン名前空間メンバ内) に位置するすべての宣言を考慮します。 その集合内に宣言がない場合は、 N 内に発見された using 指令によって表されるすべての名前空間内および N の推移的インライン名前空間メンバ内のの宣言を考慮します。 このルールは再帰的に適用されます。

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 が検索され、名前探索により AB::g が発見され、 AB::g() が選択されます
            // (A と B は検索されません)。
  AB::f(1); // まず AB が検索されます。 f はありません。
            // その後 A と B が検索されます。 名前探索により A::f と B::f が
            // 発見され (Y は検索されないため Y::f は考慮されません)、
            // オーバーロード解決が A::f(int) を選択します。
  AB::x++;    // まず AB が検索されます。 x はありません。
              // その後 A と B が検索されます。 x はありません。
              // その後 Y と Z が検索されます。 それでも x はありません。 これはエラーです。
  AB::i++;  // AB が検索されます。 i はありません。
            // その後 A と B が検索されます。 名前探索により A::i と B::i が発見されます。 これはエラーです。
  AB::h(16.8);  // まず AB が検索されます。 h はありません。
                // その後 Y と Z が検索されます。
                // 名前探索は Y::h と Z::h を発見します。 オーバーロード解決が Z::h(double) を選択します。
}

同じ宣言が2回以上発見されることは許されます。

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、 B 経由と D 経由で同じ A::a を発見します。
}

[編集] 関連項目