Spec-Zone.ru › C++

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

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

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

Если слева от :: ничего нет, поиск рассматривает только объявления в глобальной области видимости пространства имён. Это позволяет обращаться к таким именам, даже если они были скрыты локальным объявлением:

#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

Spec-Zone.ru

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