Qualified name lookup
квалифицированное
имя — это имя, которое появляется в правой части оператора разрешения области видимости
::
(см. также
квалифицированные идентификаторы
).
Квалифицированное имя может ссылаться на
- член класса (включая статические и нестатические функции, типы, шаблоны и т.д.),
- член пространства имен (включая другое пространство имен),
- перечислитель.
Если слева от
::
ничего нет, поиск рассматривает только объявления в
глобальной области пространства имен
. Это позволяет ссылаться на такие имена, даже если они были скрыты локальным объявлением:
#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 }
|
Этот раздел не завершен
Причина: остальная часть N4861 6.5.3.2[namespace.qual], попытайтесь сократить их примеры |
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 215 | C++98 |
имя перед
::
должно быть именем класса или пространства
имен, поэтому параметры шаблонов не допускались там |
имя должно обозначать класс,
пространство имен или зависимый тип |
| CWG 318 | C++98 |
если правая часть
::
называет тот же класс,
что и левая часть, квалифицированное имя всегда считалось именем конструктора этого класса |
называет конструктором только
когда это допустимо (например, не в уточненном спецификаторе типа) |