Spec-Zone.ru › C++

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

Квалифицированное имя — это имя, которое появляется справа от оператора разрешения области видимости :: (см. также квалифицированные идентификаторы). Квалифицированное имя может ссылаться на

  • член класса (включая статические и нестатические функции, типы, шаблоны и т. д.),
  • член пространства имен (включая другое пространство имен),
  • перечислитель.

Если слева от оператора :: ничего нет, поиск учитывает только объявления, сделанные в глобальном пространстве имен (или введенные в глобальное пространство имен с помощью объявления 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

Spec-Zone.ru

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