Namespaces
Variants

Qualified name lookup

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

qualified 名は、スコープ解決演算子 :: の右側に現れる名前です( qualified identifiers も参照)。 qualified名は、以下のものを参照する場合があります

  • class member (staticおよび非static関数、型、テンプレートなどを含む),
  • namespace member (別のnamespaceを含む),
  • enumerator.

:: の左側に何もない場合、ルックアップは グローバル名前空間スコープ 内の宣言のみを考慮します。これにより、ローカル宣言によって隠蔽されていた場合でも、そのような名前を参照できるようになります:

#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; // エラー: 'std'の非修飾検索が構造体を見つける
    ::std::cout << ::ok; // OK: ::stdが名前空間stdを見つける
}

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

struct A
{
    static int n;
};
int main()
{
    int A;
    A::n = 42; // OK: :: の左側の非修飾名探索は変数を無視する
    A b;       // エラー: Aの非修飾名探索は変数Aを見つける
}
template<int>
struct B : A {};
namespace N
{
    template<int>
    void B();
    int f()
    {
        return B<0>::n; // エラー: N::B<0> は型ではない
    }
}

修飾名が 宣言子 として使用される場合、同じ宣言子内でその修飾名に続く名前(ただし先行する名前は除く)の unqualified lookup は、メンバーのクラスまたは名前空間のスコープで実行されます:

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の検索で ::X が見つかり、C::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が見つかる)
}

目次

列挙子

左辺の名前の検索が 列挙型 (スコープ付きまたはスコープなし)に該当する場合、右辺の検索結果はその列挙型に属する列挙子でなければならず、そうでない場合、プログラムは不適格となります。

(C++11以降)

クラスメンバー

左側の名前の検索がクラス/構造体または共用体名に該当する場合、 :: の右側の名前は、そのクラスのスコープ内で検索されます(したがって、そのクラスまたはその基底クラスのメンバーの宣言が見つかる可能性があります)。以下の例外を除きます:

  • デストラクタは上記で説明した方法で(::の左側の名前のスコープで)検索されます。
  • ユーザー定義変換 関数名の変換型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は ::Xとして検索され、N::Xとしては検索されない

名前空間 namespace N のスコープ内での修飾名探索は、まず N 内にあるすべての宣言と、 N inline namespace members 内にあるすべての宣言を考慮します(さらに、それらのインライン名前空間メンバーのインライン名前空間メンバー内も再帰的に考慮します)。そのセット内に宣言がない場合、 N および N のすべての推移的インライン名前空間メンバー内で見つかった using-directives によって指定されるすべての名前空間内の宣言を考慮します。これらの規則は再帰的に適用されます:

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(void)が選択される
              // (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は存在しない
                 // 次にA、Bが検索される。hは存在しない
                 // 次にYとZが検索される
                 // ルックアップによりY::hとZ::hが見つかる。オーバーロード解決により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: BとDを通して同じA::aを見つける
}

不具合報告

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

DR 適用対象 公開時の動作 正しい動作
CWG 215 C++98 :: の前の名前はクラス名または名前空間名で
なければならず、テンプレートパラメータは許可されなかった
その名前はクラス、名前空間または依存型を
指定しなければならない
CWG 318 C++98 :: の右側が左側と同じクラスを指す場合、
修飾名は常にそのクラスのコンストラクタを
指すと見なされていた
許容される場合(例:詳細型指定子内でない場合)
のみコンストラクタを指す

関連項目