Поиск имени без квалификатора
Для неквалифицированного имени, то есть имени, которое не появляется справа от оператора разрешения области видимости ::, поиск имени проверяет области видимости, как описано ниже, до тех пор, пока не найдёт как минимум одно объявление любого типа, после чего поиск прекращается и дальнейшие области видимости не проверяются. (Примечание: поиск из некоторых контекстов пропускает некоторые объявления, например, поиск имени, используемого слева от :: игнорирует объявления функций, переменных и перечислений; поиск имени, используемого в качестве спецификатора базового класса, игнорирует все объявления, не являющиеся типами)
Для целей поиска неквалифицированного имени все объявления из пространства имён, обозначенного директивой 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
}
}Определение класса
Для имени, используемого где-либо в определении класса (включая спецификаторы базовых классов и определения вложенных классов), за исключением тела функции-члена, значения по умолчанию функции-члена, спецификации исключений функции-члена или инициализатора члена по умолчанию, где член может принадлежать вложенному классу, чьё определение находится в теле окружающего класса, проверяются следующие области:
Для объявления друга, поиск для определения, ссылается ли оно на ранее объявленную сущность, выполняется, как указано выше, за исключением того, что он останавливается после самого внутреннего окружающего пространства имён.
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Введённое имя класса
Для имени класса или шаблона класса, используемого в определении этого класса или шаблона или производного от него, поиск неквалифицированного имени находит класс, который определяется, как если бы имя было введено объявлением члена (с общедоступным доступом к члену). Более подробную информацию см. в injected-class-name.
Определение функции-члена
Для имени, используемого внутри тела функции-члена, значения по умолчанию функции-члена, спецификации исключений функции-члена или инициализатора члена по умолчанию, проверяемые области видимости такие же, как в определении класса, за исключением того, что вся область видимости класса рассматривается, а не только часть перед объявлением, использующей имя. Для вложенных классов проверяется всё тело окружающего класса.
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 A
{
int a;
static int s;
enum { e };
};
struct B : A, virtual V {};
struct C : A, virtual V {};
struct D : B, C {};
void f(D& pd)
{
++pd.v; // OK: only one v because only one virtual base subobject
++pd.s; // OK: only one static A::s, even though found in B and in C
int i = pd.e; // OK: only one enumerator A::e, even though found in B and C
++pd.a; // error, ambiguous: A::a in B and A::a in C
}Определение дружественной функции
Для имени, используемого в определении дружественной функции внутри тела класса, предоставляющего дружбу, поиск неквалифицированного имени выполняется так же, как для функции-члена. Для имени, используемого в дружественной функции, которая определена вне тела класса, поиск неквалифицированного имени выполняется так же, как для функции в пространстве имён.
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
}Объявление дружественной функции
Для имени, используемого в деклараторе объявления дружественной функции, которая дружит функцией-членом другого класса, если имя не является частью какого-либо аргумента шаблона в идентификаторе декларатора, поиск сначала проверяет всю область видимости функции-члена. Если не найдено в этой области видимости (или если имя является частью аргумента шаблона в идентификаторе декларатора), поиск продолжается так же, как для функции-члена класса, предоставляющего дружбу.
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
};Блок catch функции-блока try
Для имени, используемого в блоке catch функции-блока 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++23 (ISO/IEC 14882:2023):
- 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]
Отчеты о дефектах
Следующие отчеты о дефектах, изменяющих поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение, как опубликовано | Корректное поведение |
|---|---|---|---|
| CWG 490 | C++98 | любое имя в аргументе шаблона в объявлении дружественной члена-функции не искалось в области класса члена-функции | исключаются только имена в аргументах шаблона в идентификаторе декларатора |
| CWG 514 | C++98 | любое неквалифицированное имя, используемое в области пространства имен, сначала искалось в этой области | неквалифицированные имена, используемые для определения члена-переменной пространства имен вне этого пространства имен, сначала ищутся в этом пространстве имен |
См. также
- Квалифицированный поиск имени
- Область видимости
- Поиск аргументов, зависящих от области
- Вывод аргументов шаблона
- Разрешение перегрузки
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/unqualified_lookup