Поиск неквалифицированного имени
Для неквалифицированного имени, то есть имени, которое не находится справа от оператора разрешения области видимости ::, поиск имени проверяет области видимости, как описано ниже, пока не найдёт хотя бы одно объявление любого вида; после этого поиск останавливается и дальнейшие области видимости не проверяются. (Примечание: при поиске в некоторых контекстах пропускаются некоторые объявления. Например, при поиске имени, используемого слева от ::, игнорируются объявления функций, переменных и перечислителей; при поиске имени, используемого в качестве спецификатора базового класса, игнорируются все объявления, не являющиеся типами.)
Для целей поиска неквалифицированного имени все объявления из пространства имён, указанного в директиве using, рассматриваются так, как если бы они были объявлены в ближайшем внешнем пространстве имён, которое прямо или косвенно содержит и директиву using, и указанное пространство имён.
Поиск неквалифицированного имени, используемого слева от оператора вызова функции (и, что эквивалентно, оператора в выражении), описан в разделе поиск с учётом аргументов.
Область видимости файла
Для имени, используемого в глобальной области видимости (области видимости пространства имён верхнего уровня), вне любой функции, класса или объявленного пользователем пространства имён, проверяется глобальная область видимости перед местом использования имени:
int n = 1; // declaration of n int x = n + 1; // OK: lookup finds ::n int z = y - 1; // Error: lookup fails int y = 2; // declaration of y
Область видимости пространства имён
Для имени, используемого в объявленном пользователем пространстве имён вне любой функции или класса, сначала проверяется это пространство имён перед местом использования имени, затем пространство имён, в котором оно содержится, перед объявлением этого пространства имён и так далее, пока не будет достигнуто глобальное пространство имён.
int n = 1; // declaration
namespace N
{
int m = 2;
namespace Y
{
int x = n; // OK, lookup finds ::n
int y = m; // OK, lookup finds ::N::m
int z = k; // Error: lookup fails
}
int k = 3;
}
Определение вне пространства имён
Для имени, используемого в определении переменной-члена пространства имён вне этого пространства имён, поиск выполняется так же, как и для имени, используемого внутри пространства имён:
namespace X
{
extern int x; // declaration, not definition
int n = 1; // found 1st
}
int n = 2; // found 2nd
int X::x = n; // finds X::n, sets X::x to 1
Определение функции, не являющейся членом класса
Для имени, используемого в определении функции — в её теле или в качестве части аргумента по умолчанию, — если функция является членом объявленного пользователем или глобального пространства имён, сначала проверяется блок, в котором используется имя, перед местом его использования, затем внешний блок перед началом этого блока и так далее, пока не будет достигнут блок тела функции. Затем проверяется пространство имён, в котором объявлена функция, вплоть до определения (не обязательно объявления) функции, использующей это имя, затем внешние пространства имён и так далее.
namespace A
{
namespace N
{
void f();
int i = 3; // found 3rd (if 2nd is not present)
}
int i = 4; // found 4th (if 3rd is not present)
}
int i = 5; // found 5th (if 4th is not present)
void A::N::f()
{
int i = 2; // found 2nd (if 1st is not present)
while (true)
{
int i = 1; // found 1st: lookup is done
std::cout << i;
}
}
// int i; // not found
namespace A
{
namespace N
{
// int i; // not found
}
}
Определение класса
Для имени, используемого где-либо в определении класса (включая спецификаторы базовых классов и определения вложенных классов), за исключением тела функции-члена, аргумента по умолчанию функции-члена, спецификации исключений функции-члена или инициализатора нестатического члена, если член может принадлежать вложенному классу, определение которого находится в теле внешнего класса, проверяются следующие области видимости:
Для объявления friend поиск, определяющий, относится ли оно к ранее объявленной сущности, выполняется описанным выше способом, но останавливается после самого внутреннего внешнего пространства имён.
namespace M
{
// const int i = 1; // never found
class B
{
// static const int i = 3; // found 3rd (but will not pass access check)
};
}
// const int i = 5; // found 5th
namespace N
{
// const int i = 4; // found 4th
class Y : public M::B
{
// static const int i = 2; // found 2nd
class X
{
// static const int i = 1; // found 1st
int a[i]; // use of i
// static const int i = 1; // never found
};
// static const int i = 2; // never found
};
// const int i = 4; // never found
}
// const int i = 5; // never found
Внедрённое имя класса
Для имени класса или шаблона класса, используемого в определении этого класса или шаблона либо производного от него класса, поиск неквалифицированного имени находит определяемый класс так, как если бы это имя было введено объявлением члена (с открытым доступом к члену). Подробнее см. внедрённое имя класса.
Определение функции-члена
Для имени, используемого в теле функции-члена, аргументе по умолчанию функции-члена, спецификации исключений функции-члена или инициализаторе нестатического члена, проверяются те же области видимости, что и при определении класса, за исключением того, что рассматривается вся область видимости класса, а не только её часть до объявления, использующего это имя. Для вложенных классов проверяется всё тело внешнего класса.
class B
{
// int i; // found 3rd
};
namespace M
{
// int i; // found 5th
namespace N
{
// int i; // found 4th
class X : public B
{
// int i; // found 2nd
void f();
// int i; // found 2nd as well
};
// int i; // found 4th
}
}
// int i; // found 6th
void M::N::X::f()
{
// int i; // found 1st
i = 16;
// int i; // never found
}
namespace M
{
namespace N
{
// int i; // never found
}
}
В любом случае при проверке базовых классов, от которых производен класс, соблюдаются следующие правила, иногда называемые доминированием при виртуальном наследовании: Имя члена, найденное в подобъекте B, скрывает то же имя члена в любом подобъекте A, если A является подобъектом базового класса для B. (Обратите внимание, что это не скрывает имя в каких-либо дополнительных невиртуальных копиях A в графе наследования, которые не являются базовыми классами для B: это правило действует только при виртуальном наследовании.) Имена, введённые объявлениями using, рассматриваются как имена в классе, содержащем это объявление. После проверки каждого базового класса результирующий набор должен включать либо объявления статического члена из подобъектов одного типа, либо объявления нестатических членов из одного и того же подобъекта. | (до C++11) |
Формируется набор поиска, состоящий из объявлений и подобъектов, в которых эти объявления найдены. Объявления using заменяются обозначаемыми ими членами, а объявления типов, включая внедрённые имена классов, заменяются типами, которые они обозначают. Если C — класс, в области видимости которого использовано имя, сначала проверяется C. Если список объявлений в C пуст, для каждого из его непосредственных базовых классов Bi формируется набор поиска (при этом эти правила применяются рекурсивно, если у Bi есть собственные базовые классы). После формирования наборы поиска непосредственных базовых классов объединяются с набором поиска в C следующим образом:
| (начиная с C++11) |
struct X { void f(); };
struct B1: virtual X { void f(); };
struct B2: virtual X {};
struct D : B1, B2
{
void foo()
{
X::f(); // OK, calls X::f (qualified lookup)
f(); // OK, calls B1::f (unqualified lookup)
}
};
// C++98 rules: B1::f hides X::f, so even though X::f can be reached from D
// through B2, it is not found by name lookup from D.
// C++11 rules: lookup set for f in D finds nothing, proceeds to bases
// lookup set for f in B1 finds B1::f, and is completed
// merge replaces the empty set, now lookup set for f in C has B1::f in B1
// lookup set for f in B2 finds nothing, proceeds to bases
// lookup for f in X finds X::f
// merge replaces the empty set, now lookup set for f in B2 has X::f in X
// merge into C finds that every subobject (X) in the lookup set in B2 is a base
// of every subobject (B1) already merged, so the B2 set is discarded
// C is left with just B1::f found in B1
// (if struct D : B2, B1 was used, then the last merge would *replace* C's
// so far merged X::f in X because every subobject already added to C (that is X)
// would be a base of at least one subobject in the new set (B1), the end
// result would be the same: lookup set in C holds just B1::f found in B1)
Поиск неквалифицированного имени, находящий статические члены B, вложенные типы B и перечислители, объявленные в B, не является неоднозначным, даже если в дереве наследования проверяемого класса имеется несколько невиртуальных базовых подобъектов типа B: struct V { int v; };
struct B
{
int a;
static int s;
enum { e };
};
struct B1 : B, virtual V {};
struct B2 : B, virtual V {};
struct D : B1, B2 {};
void f(D& pd)
{
++pd.v; // OK: only one v because only one virtual base subobject
++pd.s; // OK: only one static B::s, even though found in both B1 and B2
int i = pd.e; // OK: only one enumerator B::e, even though found in both B1 and B2
++pd.a; // error, ambiguous: B::a in B1 and B::a in B2
}
Определение дружественной функции
Для имени, используемого в определении функции friend внутри тела класса, предоставляющего дружбу, поиск неквалифицированного имени выполняется так же, как для функции-члена. Для имени, используемого в функции friend, определённой вне тела класса, поиск неквалифицированного имени выполняется так же, как для функции в пространстве имён.
int i = 3; // found 3rd for f1, found 2nd for f2
struct X
{
static const int i = 2; // found 2nd for f1, never found for f2
friend void f1(int x)
{
// int i; // found 1st
i = x; // finds and modifies X::i
}
friend int f2();
// static const int i = 2; // found 2nd for f1 anywhere in class scope
};
void f2(int x)
{
// int i; // found 1st
i = x; // finds and modifies ::i
}
Объявление дружественной функции
Для имени, используемого в деклараторе объявления функции friend, которая объявляет дружественной функцию-член другого класса, если имя не является частью какого-либо аргумента шаблона в идентификаторе декларатора, неквалифицированный поиск сначала проверяет всю область видимости класса функции-члена. Если в этой области видимости имя не найдено (или если имя является частью аргумента шаблона в идентификаторе декларатора), поиск продолжается так же, как для функции-члена класса, предоставляющего дружбу.
template<class T>
struct S;
// the class whose member functions are friended
struct A
{
typedef int AT;
void f1(AT);
void f2(float);
template<class T>
void f3();
void f4(S<AT>);
};
// the class that is granting friendship for f1, f2 and f3
struct B
{
typedef char AT;
typedef float BT;
friend void A::f1(AT); // lookup for AT finds A::AT (AT found in A)
friend void A::f2(BT); // lookup for BT finds B::BT (BT not found in A)
friend void A::f3<AT>(); // lookup for AT finds B::AT (no lookup in A, because
// AT is in the declarator identifier A::f3<AT>)
};
// the class template that is granting friendship for f4
template<class AT>
struct C
{
friend void A::f4(S<AT>); // lookup for AT finds A::AT
// (AT is not in the declarator identifier A::f4)
};
Аргумент по умолчанию
Для имени, используемого в аргументе по умолчанию в объявлении функции, или имени, используемого в части выражения инициализатора члена конструктора, сначала находятся имена параметров функции, прежде чем проверяются области видимости внешнего блока, класса или пространства имён:
class X
{
int a, b, i, j;
public:
const int& r;
X(int i): r(a), // initializes X::r to refer to X::a
b(i), // initializes X::b to the value of the parameter i
i(i), // initializes X::i to the value of the parameter i
j(this->i) // initializes X::j to the value of X::i
{}
};
int a;
int f(int a, int b = a); // error: lookup for a finds the parameter a, not ::a
// and parameters are not allowed as default arguments
Определение статического члена-данных
Для имени, используемого в определении статического члена-данных, поиск выполняется так же, как для имени, используемого в определении функции-члена.
struct X
{
static int x;
static const int n = 1; // found 1st
};
int n = 2; // found 2nd
int X::x = n; // finds X::n, sets X::x to 1, not 2
Объявление перечислителя
Для имени, используемого в части инициализатора объявления перечислителя, сначала находятся ранее объявленные перечислители в том же перечислении, а затем поиск неквалифицированного имени продолжается во внешней области видимости блока, класса или пространства имён.
const int RED = 7;
enum class color
{
RED,
GREEN = RED + 2, // RED finds color::RED, not ::RED, so GREEN = 2
BLUE = ::RED + 4 // qualified lookup finds ::RED, BLUE = 11
};
Обработчик блока try функции
Для имени, используемого в обработчике блока try функции, поиск выполняется так, как если бы имя использовалось в самом начале внешнего блока тела функции (в частности, параметры функции видны, а имена, объявленные в этом внешнем блоке, — нет).
int n = 3; // found 3rd
int f(int n = 2) // found 2nd
try
{
int n = -1; // never found
}
catch(...)
{
// int n = 1; // found 1st
assert(n == 2); // loookup for n finds function parameter f
throw;
}
Перегруженный оператор
Для оператора, используемого в выражении (например, operator+, используемого в a + b), правила поиска немного отличаются от правил для оператора, используемого в явном выражении вызова функции, таком как operator+(a, b): при разборе выражения выполняются два отдельных поиска — для перегрузок оператора, не являющихся членами, и для перегрузок оператора-члена (для операторов, допускающих обе формы). Затем эти наборы объединяются со встроенными перегрузками оператора на равных основаниях, как описано в разделе разрешение перегрузки. Если используется синтаксис явного вызова функции, выполняется обычный поиск неквалифицированного имени:
struct A {};
void operator+(A, A); // user-defined non-member operator+
struct B
{
void operator+(B); // user-defined member operator+
void f();
};
A a;
void B::f() // definition of a member function of B
{
operator+(a, a); // error: regular name lookup from a member function
// finds the declaration of operator+ in the scope of B
// and stops there, never reaching the global scope
a + a; // OK: member lookup finds B::operator+, non-member lookup
// finds ::operator+(A, A), overload resolution selects ::operator+(A, A)
}
Определение шаблона
Для независимого от параметров шаблона имени, используемого в определении шаблона, поиск неквалифицированного имени выполняется при рассмотрении определения шаблона. Связь с объявлениями, существующими в этот момент, не зависит от объявлений, видимых в точке инстанцирования. Для зависимого имени, используемого в определении шаблона, поиск откладывается до тех пор, пока не станут известны аргументы шаблона; после этого ADL проверяет объявления функций с внешним связыванием(до C++11), видимые в контексте определения шаблона, а также в контексте инстанцирования шаблона, тогда как поиск без ADL проверяет только объявления функций с внешним связыванием(до C++11), видимые в контексте определения шаблона (иными словами, добавление нового объявления функции после определения шаблона не делает его видимым, за исключением случаев, когда оно обнаруживается через ADL). Поведение не определено, если в пространствах имён, проверяемых поиском ADL, имеется более подходящее объявление с внешним связыванием, объявленное в другой единице трансляции, или если при проверке этих единиц трансляции поиск был бы неоднозначным. В любом случае, если базовый класс зависит от параметра шаблона, его область видимости не проверяется при поиске неквалифицированного имени — ни в точке определения, ни в точке инстанцирования.
void f(char); // first declaration of f
template<class T>
void g(T t)
{
f(1); // non-dependent name: lookup finds ::f(char) and binds it now
f(T(1)); // dependent name: lookup postponed
f(t); // dependent name: lookup postponed
// dd++; // non-dependent name: lookup finds no declaration
}
enum E { e };
void f(E); // second declaration of f
void f(int); // third declaration of f
double dd;
void h()
{
g(e); // instantiates g<E>, at which point
// the second and the third uses of the name 'f'
// are looked up and find ::f(char) (by lookup) and ::f(E) (by ADL)
// then overload resolution chooses ::f(E).
// This calls f(char), then f(E) twice
g(32); // instantiates g<int>, at which point
// the second and the third uses of the name 'f'
// are looked up and find ::f(char) only
// then overload resolution chooses ::f(char)
// This calls f(char) three times
}
typedef double A;
template<class T>
class B
{
typedef int A;
};
template<class T>
struct X : B<T>
{
A a; // lookup for A finds ::A (double), not B<T>::A
};
Примечание: обоснование и последствия этого правила см. в разделе правила поиска зависимых имён.
Имя шаблона
Член шаблона класса вне шаблона
Отчёты об ошибках стандарта
Следующие отчёты об ошибках, изменяющие поведение, были ретроспективно применены к ранее опубликованным стандартам C++.
| Отчёт об ошибке | Применён к | Поведение согласно опубликованной версии | Правильное поведение |
|---|---|---|---|
| CWG 490 | C++98 | любое имя в аргументе шаблона в объявлении дружественной функции-члена не искалось в области видимости класса функции-члена | исключаются только имена в аргументах шаблона в идентификаторе декларатора |
| CWG 514 | C++98 | любое неквалифицированное имя, используемое в области видимости пространства имён, сначала искалось в этой области | неквалифицированные имена, используемые для определения переменной-члена пространства имён вне этого пространства имён, сначала ищутся в этом пространстве имён |
Литература
- Стандарт C++23 (ISO/IEC 14882:2024):
- 6.5 Поиск имён [basic.lookup] (стр. 44–45)
- 6.5.2 Поиск имён членов [class.member.lookup] (стр. 45–47)
- 13.8 Разрешение имён [temp.res] (стр. 399–403)
- Стандарт C++20 (ISO/IEC 14882:2020):
- 6.5 Поиск имён [basic.lookup] (стр. 38–50)
- 11.8 Поиск имён членов [class.member.lookup] (стр. 283–285)
- 13.8 Разрешение имён [temp.res] (стр. 385–400)
- Стандарт C++17 (ISO/IEC 14882:2017):
- 6.4 Поиск имён [basic.lookup] (стр. 50–63)
- 13.2 Поиск имён членов [class.member.lookup] (стр. 259–262)
- 17.6 Разрешение имён [temp.res] (стр. 375–378)
- Стандарт C++14 (ISO/IEC 14882:2014):
- 3.4 Поиск имён [basic.lookup] (стр. 42–56)
- 10.2 Поиск имён членов [class.member.lookup] (стр. 233–236)
- 14.6 Разрешение имён [temp.res] (стр. 346–359)
- Стандарт C++11 (ISO/IEC 14882:2011):
- 3.4 Поиск имён [basic.lookup]
- 10.2 Поиск имён членов [class.member.lookup]
- 14.6 Разрешение имён [temp.res]
- Стандарт C++98 (ISO/IEC 14882:1998):
- 3.4 Поиск имён [basic.lookup]
- 10.2 Поиск имён членов [class.member.lookup]
- 14.6 Разрешение имён [temp.res]
См. также
- Поиск квалифицированного имени
- Область видимости
- Поиск с учётом аргументов
- Выведение аргументов шаблона
- Разрешение перегрузки
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/unqualified_lookup