Поиск квалифицированного имени
Квалифицированное имя — это имя, которое появляется справа от оператора разрешения области видимости :: (см. также квалифицированные идентификаторы). Квалифицированное имя может ссылаться на
- член класса (включая статические и нестатические функции, типы, шаблоны и т. д.),
- член пространства имен (включая другое пространство имен),
- перечислитель.
Если слева от оператора :: ничего нет, поиск учитывает только объявления, сделанные в глобальном пространстве имен (или введенные в глобальное пространство имен с помощью объявления using). Это позволяет ссылаться на такие имена даже если они были скрыты локальным объявлением:
#include <iostream>
int main()
{
struct std {};
std::cout << "fail\n"; // Error: unqualified lookup for 'std' finds the struct
::std::cout << "ok\n"; // OK: ::std finds the namespace std
}Перед поиском имени справа от оператора :: должен быть завершен поиск имени слева от него (если не используется выражение decltype, или слева ничего нет). Этот поиск, который может быть квалифицированным или неквалифицированным, в зависимости от того, есть ли другой :: слева от этого имени, учитывает только пространства имен, типы классов, перечисления и шаблоны, специализации которых являются типами. Если найденное имя слева не обозначает пространство имен или класс, перечисление или зависимый тип, программа является некорректной:
struct A
{
static int n;
};
int main()
{
int A;
A::n = 42; // OK: unqualified lookup of A to the left of :: ignores the variable
A b; // Error: unqualified lookup of A finds the variable A
}
template<int>
struct B : A {};
namespace N
{
template<int>
void B();
int f()
{
return B<0>::n; // Error: N::B<0> is not a type
}
}Когда квалифицированное имя используется как декларатор, тогда неквалифицированный поиск имен, используемых в том же деклараторе после квалифицированного имени, но не тех, что перед ним, выполняется в области видимости класса или пространства имен члена:
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]; // Error: look up for X finds ::X, not C::X
C::X C::arr[number], brr[number]; // OK: size of arr is 50, size of brr is 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(); // The name I after ~ is looked up in the same scope as I before ::
// (that is, within the scope of C, so it finds C::I)
q->I1::~I2(); // The name I2 is looked up in the same scope as I1
// (that is, from the current scope, so it finds ::I2)
AB x;
x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before ::
// (that is, from the current scope, so it finds ::AB)
}
ПеречислителиЕсли поиск имени слева приводит к перечислению (скопированному или нескопированному), поиск имени справа должен привести к перечислителю, который принадлежит этому перечислению, иначе программа является некорректной. |
(с C++11) |
Члены класса
Если поиск имени слева приводит к имени класса/структуры или объединения, имя справа от оператора :: ищется в области видимости этого класса (и, таким образом, может найти объявление члена этого класса или его базового класса), с следующими исключениями:
- Деструктор ищется, как описано выше (в области видимости имени слева от ::).
- Тип-идентификатор преобразования в функции пользовательского преобразования сначала ищется в области видимости класса. Если не найдено, имя затем ищется в текущей области видимости.
- Имена, используемые в аргументах шаблона, ищутся в текущей области видимости (а не в области видимости имени шаблона).
- Имена в объявлениях using также учитывают имена классов/перечислений, которые скрыты именем переменной, члена данных, функции или перечислителя, объявленных в той же области видимости.
Если правая часть оператора :: называет тот же класс, что и левая часть, имя обозначает конструктор этого класса. Такое квалифицированное имя может использоваться только в объявлении конструктора и в объявлении using для наследуемого конструктора. В тех поисках, где имена функций игнорируются (то есть, при поиске имени слева от оператора ::, при поиске имени в спецификаторе типа с подробными сведениями или спецификаторе базового класса), тот же синтаксис приводит к названию введенного класса:
struct A { A(); };
struct B : A { B(); };
A::A() {} // A::A names a constructor, used in a declaration
B::B() {} // B::B names a constructor, used in a declaration
B::A ba; // B::A names the type A (looked up in the scope of B)
A::A a; // Error: A::A does not name a type
struct A::A a2; // OK: lookup in elaborated type specifier ignores functions
// so A::A simply names the class A as seen from within the scope of A
// (that is, the injected-class-name)Квалифицированный поиск имени может использоваться для доступа к члену класса, который скрыт вложенным объявлением или производным классом. Вызов квалифицированной функции члена никогда не является виртуальным:
struct B { virtual void foo(); };
struct D : B { void foo() override; };
int main()
{
D x;
B& b = x;
b.foo(); // Calls D::foo (virtual dispatch)
b.B::foo(); // Calls B::foo (static dispatch)
}Члены пространства имен
Если имя слева от оператора :: относится к пространству имен или если слева от оператора :: ничего нет (в этом случае оно относится к глобальному пространству имен), имя, которое появляется справа от оператора ::, ищется в области видимости этого пространства имен, за исключением
- имен, используемых в аргументах шаблона, ищутся в текущей области видимости:
namespace N
{
template<typename T>
struct foo {};
struct X {};
}
N::foo<X> x; // Error: X is looked up as ::X, not as 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 is searched, AB::g found by lookup and is chosen AB::g(void)
// (A and B are not searched)
AB::f(1); // First, AB is searched. There is no f
// Then, A, B are searched
// A::f, B::f found by lookup
// (but Y is not searched so Y::f is not considered)
// Overload resolution picks A::f(int)
AB::x++; // First, AB is searched. There is no x
// Then A, B are searched. There is no x
// Then Y and Z are searched. There is still no x: this is an error
AB::i++; // AB is searched. There is no i
// Then A, B are searched. A::i and B::i found by lookup: this is an error
AB::h(16.8); // First, AB is searched. There is no h
// Then A, B are searched. There is no h
// Then Y and Z are searched
// Lookup finds Y::h and Z::h. Overload resolution picks 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: finds the same A::a through B and through D
}Отчеты об ошибках
Следующие отчеты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применимо к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 215 | C++98 | имя, предшествующее :: должно быть именем класса или пространства именимя, поэтому параметры шаблона там не разрешались |
имя должно обозначать класс, пространство имен или зависимый тип |
| CWG 318 | C++98 | если правая часть оператора :: называет тот же класс,что и левая часть, квалифицированное имя всегда считалось именем конструктора этого класса |
называть конструктор только в приемлемых случаях (например, не в спецификаторе типа с подробными сведениями) |
См. также
- Поиск неквалифицированного имени
- Область видимости
- Поиск, зависящий от аргумента
- Вывод аргумента шаблона
- Разрешение перегрузки
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/qualified_lookup