Поиск квалифицированного имени
Квалифицированное имя — это имя, которое находится справа от оператора разрешения области видимости :: (см. также квалифицированные идентификаторы). Квалифицированное имя может обозначать
- член класса (включая статические и нестатические функции, типы, шаблоны и т. д.),
- член пространства имён (включая другое пространство имён),
- элемент перечисления.
Если слева от :: ничего нет, поиск рассматривает только объявления в глобальной области видимости пространства имён. Это позволяет обращаться к таким именам, даже если они были скрыты локальным объявлением:
#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; // Error: unqualified lookup for 'std' finds the struct
::std::cout << ::ok; // 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/cpp/language/qualified_lookup