Spec-Zone.ru › C++

Поиск неквалифицированного имени

Для неквалифицированного имени, то есть имени, которое не находится справа от оператора разрешения области видимости ::, поиск имени проверяет области видимости, как описано ниже, пока не найдёт хотя бы одно объявление любого вида; после этого поиск останавливается и дальнейшие области видимости не проверяются. (Примечание: при поиске в некоторых контекстах пропускаются некоторые объявления. Например, при поиске имени, используемого слева от ::, игнорируются объявления функций, переменных и перечислителей; при поиске имени, используемого в качестве спецификатора базового класса, игнорируются все объявления, не являющиеся типами.)

Для целей поиска неквалифицированного имени все объявления из пространства имён, указанного в директиве 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
    }
}

Определение класса

Для имени, используемого где-либо в определении класса (включая спецификаторы базовых классов и определения вложенных классов), за исключением тела функции-члена, аргумента по умолчанию функции-члена, спецификации исключений функции-члена или инициализатора нестатического члена, если член может принадлежать вложенному классу, определение которого находится в теле внешнего класса, проверяются следующие области видимости:

a) тело класса, в котором используется имя, вплоть до места его использования;
b) всё тело его базового класса (классов), с рекурсивным переходом к их базовым классам, если объявления не найдены;
c) если этот класс является вложенным, тело внешнего класса вплоть до определения этого класса и всё тело базового класса (классов) внешнего класса;
d) если этот класс является локальным или вложен в локальный класс, область видимости блока, в котором определён класс, вплоть до места его определения;
e) если этот класс является членом пространства имён, вложен в класс, являющийся членом пространства имён, или является локальным классом в функции, которая является членом пространства имён, проверяется область видимости пространства имён вплоть до определения класса, внешнего класса или функции; поиск продолжается во внешних пространствах имён вплоть до глобальной области видимости.

Для объявления 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 следующим образом:
  • если набор объявлений в Bi пуст, он отбрасывается;
  • если сформированный к этому моменту набор поиска для C пуст, он заменяется набором поиска для Bi;
  • если каждый подобъект в наборе поиска для Bi является базовым классом хотя бы одного из подобъектов, уже добавленных в набор поиска для C, набор поиска для Bi отбрасывается;
  • если каждый подобъект, уже добавленный в набор поиска для C, является базовым классом хотя бы одного подобъекта в наборе поиска для Bi, набор поиска для C отбрасывается и заменяется набором поиска для Bi;
  • в противном случае, если наборы объявлений в Bi и C различаются, результатом является неоднозначное объединение: новый набор поиска для C содержит недопустимое объявление и объединение подобъектов, ранее объединённых в C, с подобъектами, добавленными из Bi. Этот недопустимый набор поиска может не приводить к ошибке, если впоследствии он будет отброшен;
  • в противном случае новый набор поиска для C содержит общие наборы объявлений и объединение подобъектов, ранее объединённых в C, с подобъектами, добавленными из Bi.
(начиная с 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API