close
Пространства имён
Варианты
Действия

Поиск квалифицированного имени (полный)

Материал из cppreference.com
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
 

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

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

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

#include <iostream>
int main() {
  struct std{};
  std::cout << "неудача\n"; // Ошибка: неквалифицированный поиск для 'std'
                            // находит структуру
  ::std::cout << "ok\n";    // OK: ::std находит пространство имён std
}

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

struct A
{
  static int n;
};

int main()
{
  int A;
  A::n = 42;    // OK: неквалифицированный поиск A слева от :: игнорирует переменную
  A b;          // Ошибка: неквалифицированный поиск A находит переменную A
}

template<int>
struct B : A {};

namespace N
{
    template<int>
    void B();

    int f()
    {
        return B<0>::n; // ошибка: N::B<0> не является типом
    }
}

Когда полное имя используется в качестве декларатора, тогда неквалифицированный поиск имён, используемых в том же деклараторе, которые следуют за этим полным именем, но не имён, которые предшествуют ему, выполняется в области элементов класса или пространства имён:

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];    // Ошибка: поиск для X находит ::X, не C::X
C::X C::arr[number], brr[number]; // OK: размер arr 50, размер brr 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(); // имя I после ~ ищется в той же области видимости, что и I перед ::
                 // (то есть в области видимости C, поэтому находится C::I)
  q->I1::~I2();  // Имя I2 ищется в той же области видимости, что и I1,
                 // то есть в текущей области видимости, поэтому находится ::I2
  AB x;
  x.AB::~AB();   // Имя AB после ~ ищется в той же области видимости, что и AB перед :: ,
                 // то есть в текущей области видимости, поэтому находится ::AB
}

Перечислители

Если поиск имени в левой части даёт перечисление (с областью видимости или без), поиск в правой части должен приводить к перечислителю, который принадлежит этому перечислению, в противном случае программа некорректна.

(начиная с C++11)

Элементы класса

Если поиск в левой части имени даёт имя класса/структуры или объединения, имя в правой части :: ищется в области видимости этого класса (и поэтому может быть найдено объявление элемента этого класса или его базового класса) со следующими исключениями:

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

Если правая часть :: именует тот же класс, что и левая часть, имя обозначает конструктор этого класса. Такое полное имя может использоваться только в объявлении конструктора и в using-объявлении для наследуемого конструктора. В тех поисках, где имена функций игнорируются (то есть при поиске имени слева от ::, при поиске имени в конкретизированном спецификаторе типа или спецификаторе базового класса), тот же синтаксис преобразуется в имя внедрённого класса:

struct A { A(); };
struct B : A { B(); };
A::A() { } // A::A именует конструктор, используемый в объявлении
B::B() { } // B::B именует конструктор, используемый в объявлении
B::A ba;   // B::A именует тип A (поиск в области видимости B)
A::A a;    // Ошибка, A::A не именует тип

struct A::A a2; // OK: поиск в конкретизированном спецификаторе типа игнорирует функции,
                // поэтому A::A просто именует класс A, как видно из области видимости A
                // (то есть имя внедрённого класса)

Поиск квалифицированного имени может использоваться для доступа к элементу класса, который скрыт вложенным объявлением или производным классом. Вызов квалифицированной функции-элемента никогда не бывает виртуальным

struct B { virtual void foo(); };
struct D : B { void foo() override; };
int main()
{
    D x;
    B& b = x;
    b.foo(); // вызывает D::foo (виртуальная диспетчеризация)
    b.B::foo(); // вызывает B::foo (статическая диспетчеризация)
}

Элементы пространства имён

Если имя слева от :: ссылается на пространство имён или если слева от :: ничего нет (в этом случае оно ссылается на глобальное пространство имён), имя, которое появляется справа от ::, ищется в области этого пространства имён, за исключением того, что

  • имена, используемые в аргументах шаблона, ищутся в текущей области видимости:
namespace N {
   template<typename T> struct foo {};
   struct X {};
}
N::foo<X> x; // ошибка: X рассматривается как ::X, а не как 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, AB::g найдено поиском и выбирается AB::g(void)
            // (в A и B поиск не выполняется)
  AB::f(1); // Сначала выполняется поиск в AB, f нет
            // Затем выполняется поиск в A и B
            // A::f, B::f найдены поиском (но в Y поиск не выполняется,
            // поэтому Y::f не рассматривается)
            // разрешение перегрузки выбирает A::f(int)
  AB::x++;    // Сначала выполняется поиск в AB, x не найден
              // Затем выполняется поиск в A и B. x не найден
              // Затем выполняется поиск в Y и Z. x по-прежнему нет: это ошибка
  AB::i++;  // Выполняется поиск в AB, i не найден
            // Затем выполняется поиск в A и B. Найдены поиском A::i и B::i: это ошибка
  AB::h(16.8);  // Сначала выполняется поиск в AB: h не найден
                // Затем выполняется поиск в A и B. h не найден
                // Затем выполняется поиск в Y и Z.
                // поиск находит Y::h и Z::h. Разрешение перегрузки выбирает 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: находит одинаковые A::a через B и через D
}

Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 215 C++98 имя, предшествующее ::, должно быть
именем класса или именем пространства имён,
поэтому параметры шаблона здесь не допускаются
имя должно обозначать пространство
имён, класс или зависимый тип
CWG 318 C++98 если правая часть :: именует тот же класс, что и
левая часть, полное имя всегда считалось именем
конструктора этого класса
именует конструктор только тогда,
когда это приемлемо (например, не в
подробном спецификаторе типа)

Смотрите также