Введённое имя класса
Введённое имя класса это неквалифицированное имя класса внутри области видимости этого класса .
В шаблоне класса, введённое имя класса может быть использовано либо как имя шаблона, которое ссылается на текущий шаблон, либо как имя класса, которое ссылается на текущий экземпляр.
Содержание |
[править] Объяснение
В области видимости класса, имя текущего класса обрабатывается как если бы оно было именем открытого элемента; это и называется введённое имя класса. Точка объявления имени находится сразу после открывающей скобки определения класса.
int X; struct X { void f() { X* p; // OK. X ссылается на введённое имя класса ::X* q; // Ошибка: поиск имени находит имя переменной, // которое скрывает имя структуры } };
Как и другие элементы, введённое имя класса наследуется. При наличии частного или защищённого наследования, введённое имя класса является непрямым базовым классом и может оказаться недоступным в классе наследнике.
struct A {}; struct B : private A {}; struct C : public B { A* p; // Ошибка: введённое имя класса A недоступно ::A* q; // OK, не испльзует введённое имя класса };
[править] В шаблонах класса
Как в других классах, шаблоны имеют введённое имя класса. Введённое имя класса может быть использовано как имя шаблона или имя типа.
В следующих случаях, введённое имя класса обрабатывается как имя шаблона класса, служащее именем шаблона для самого шаблона класса:
- за ним следует
<
- оно используется как аргумент шаблона шаблона
- это последний идентификатор в уточнённом спецификаторе класса объявления шаблона дружественного класса.
В ином случае оно рассматривается как имя типа и эквивалентно имени шаблона, за которым следуют параметры шаблона шаблона класса, заключенные в <>
.
template <template <class, class> class> struct A; template<class T1, class T2> struct X { X<T1, T2>* p; // OK, X обрабатывается как имя шаблона using a = A<X>; // OK, X обрабатывается как имя шаблона template<class U1, class U2> friend class X; // OK, X обрабатывается как имя шаблона X* q; // OK, X обрабатывается как имя типа, равнозначное X<T1, T2> };
Внутри области видимости специализации шаблона класса или внутри частичной специализации, если ввёденное имя класса было использовано как имя типа, это равнозначно имени шаблона, за которым следуют аргументы шаблона специализации шаблона класса или частичной специализации, заключённые в <>
.
template<> struct X<void, void> { X* p; // OK, X обрабатывается как имя типа, равнозначное X<void, void> template<class, class> friend class X; // OK, X обрабатывается как имя шаблона // (так-же как и в главном шаблоне) X<void, void>* q; // OK, X обрабатывается как имя шаблона }; template<class T> struct X<char, T> { X* p, q; // OK, X обрабатывается как имя типа, равнозначное X<char, T> using r = X<int, int>; // OK, может быть использовано для того // чтобы именовать другую специализацию };
Введённое имя класса шаблона класса или специализации шаблона класса может быть использовано либо как имя шаблона, либо как имя типа, где бы оно ни было внутри области видимости.
template<> class X<int, char> { class B { X a; // обозначает X<int, char> template<class,class> friend class X; // обозначает ::X }; }; template <class T> struct Base { Base* p; // OK: Base обозначает Base<T> }; template <class T> struct Derived : public Base<T*> { typename Derived::Base* p; // OK: Derived::Base значит Derived<T>::Base, // которое есть Base<T*> }; template<class T, template<class> class U = T::template Base> struct Third { }; Third<Derived<int>> t; // OK: аргумент по умолчанию использует введённое имя класса // как шаблон
Поиск, который находит введённое имя класса, может привести к неоднозначности в некоторых случаях (например, если оно найдено более чем в одном базовом классе). Если все найденные введённые имена классов относятся к специализациям одного и того же шаблона класса, и если это имя используется в качестве имени шаблона, ссылка относится к самому шаблону класса, а не к его специализации, и не является двусмысленной.
template <class T> struct Base {}; template <class T> struct Derived: Base<int>, Base<char> { typename Derived::Base b; // ошибка: двусмысленность typename Derived::Base<double> d; // OK };
[править] Введённое имя класса и конструкторы
Конструкторы не имеют имён, но введенное имя класа в окружении класса рассматривается как объявление или определение имени конструктора.
В полном имени C::D
, если
- поиск имени не игнорирует имена функций, и
- поиск
D
в области видимости классаC
находит его введённое имя класса
Квалифицированное имя всегда рассматривается как имя конструктора для C
. Такое имя может быть использовано только в объявлении конструктора (например в объявлении дружественного конструктора, специализации шаблона конструктора, создании экземпляра шаблона конструктора, или в определении конструктора) или использовано в унаследованном конструкторе (начиная с C++11).
struct A { A(); A(int); template<class T> A(T) {} }; using A_alias = A; A::A() {} A_alias::A(int) {} template A::A(double); struct B : A { using A_alias::A; }; A::A a; // Ошибка: A::A рассматривается как имя конструктора, а не как тип struct A::A a2; // OK, аналогично 'A a2;' B::A b; // OK, аналогично 'A b;'
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 1004 | C++98 | введённое имя класса не может быть аргументом шаблона шаблона | разрешено, в данном случае оно относится к самому шаблону класса |