Поиск квалифицированного имени (полный)
Квалифицированное имя это имя, которое появляется справа от оператора разрешения области видимости :: (смотрите также квалифицированные идентификаторы).
Полное имя может ссылаться на
- элемент класса (включая статические и нестатические функции, типы, шаблоны и т.д.)
- элемент пространства имён (включая другое пространство имён)
- перечислитель
Если в левой части :: ничего нет, поиск учитывает только объявления, сделанные в области глобального пространства имён (или введённые в глобальное пространство имён с помощью объявления using). Это позволяет ссылаться на такие имена, даже если они были скрыты локальным объявлением:
#include <iostream>
int main() {
struct std{};
std::cout << "неудача\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
}
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) |
Элементы класса
Если поиск в левой части имени даёт имя класса/структуры или объединения, имя в правой части :: ищется в области видимости этого класса (и поэтому может быть найдено объявление элемента этого класса или его базового класса) со следующими исключениями:
- Деструктор ищется, как описано выше (в области видимости имени слева от ::).
- Идентификатор типа преобразования в имени функции преобразования, определяемой пользователем сначала ищется в области видимости класса. Если не найден, то имя ищется в текущей области видимости.
- Имена, используемые в аргументах шаблона, ищутся в текущей области видимости (не в области видимости имени шаблона).
- Имена в 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
Квалифицированный поиск в области видимости пространства имён 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++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 215 | C++98 | имя, предшествующее ::, должно бытьименем класса или именем пространства имён, поэтому параметры шаблона здесь не допускаются |
имя должно обозначать пространство имён, класс или зависимый тип |
| CWG 318 | C++98 | если правая часть :: именует тот же класс, что илевая часть, полное имя всегда считалось именем конструктора этого класса |
именует конструктор только тогда, когда это приемлемо (например, не в подробном спецификаторе типа) |