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

квалифицированное имя — это имя, которое появляется в правой части оператора разрешения области видимости :: (см. также квалифицированные идентификаторы ). Квалифицированное имя может ссылаться на

  • член класса (включая статические и нестатические функции, типы, шаблоны и т.д.),
  • член пространства имен (включая другое пространство имен),
  • перечислитель.

Если слева от :: ничего нет, поиск рассматривает только объявления в глобальной области пространства имен . Это позволяет ссылаться на такие имена, даже если они были скрыты локальным объявлением:

#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 слева от :: игнорирует переменную
    A b;       // Ошибка: поиск A находит переменную A
}
template<int>
struct B : A {};
namespace N
{
    template<int>
    void B();
    int f()
    {
        return B<0>::n; // Ошибка: N::B<0> не является типом
    }
}

Когда квалифицированное имя используется как декларатор , тогда неполный поиск имён, используемых в том же деклараторе, которые следуют за этим квалифицированным именем, но не имён, которые предшествуют ему, выполняется в области видимости класса или пространства имён члена:

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)

Члены класса

Если поиск имени левой части приводит к имени класса/структуры или объединения, имя в правой части :: ищется в области видимости этого класса (и, таким образом, может найти объявление члена этого класса или его базового класса), за следующими исключениями:

  • Деструктор ищется, как описано выше (в области видимости имени слева от ::).
  • conversion-type-id в имени пользовательской функции преобразования сначала ищется в области видимости класса. Если не найден, имя затем ищется в текущей области видимости.
  • Имена, используемые в аргументах шаблона, ищутся в текущей области видимости (не в области видимости имени шаблона).
  • Имена в using-декларациях также учитывают имена классов/перечислений, которые скрыты именем переменной, элемента данных, функции или перечислителя, объявленного в той же области видимости.

Если правая часть :: именует тот же класс, что и левая часть, это имя обозначает конструктор этого класса. Такое квалифицированное имя может использоваться только в объявлении конструктора и в using-декларации для наследуемого конструктора . В тех поисках, где имена функций игнорируются (то есть при поиске имени слева от :: , при поиске имени в уточненном спецификаторе типа или спецификаторе базового класса ), тот же синтаксис разрешается в injected-class-name:

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
                // (то есть, injected-class-name)

Квалифицированный поиск имени может использоваться для доступа к члену класса, который скрыт вложенным объявлением или производным классом. Вызов квалифицированной функции-члена никогда не является виртуальным:

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

Квалифицированный поиск в области видимости пространства имён N сначала рассматривает все объявления, расположенные в N и все объявления, расположенные в встроенных пространствах имён N (и, транзитивно, в их встроенных пространствах имён). Если в этом наборе нет объявлений, тогда он рассматривает объявления во всех пространствах имён, указанных директивами using , найденными в N и во всех транзитивных встроенных пространствах имён 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(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: находит тот же A::a через B и через D
}

Отчеты о дефектах

Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 215 C++98 имя перед :: должно быть именем класса или пространства
имен, поэтому параметры шаблонов не допускались там
имя должно обозначать класс,
пространство имен или зависимый тип
CWG 318 C++98 если правая часть :: называет тот же класс,
что и левая часть, квалифицированное имя всегда
считалось именем конструктора этого класса
называет конструктором только
когда это допустимо (например, не в
уточненном спецификаторе типа)

Смотрите также