Injected-class-name
Внедренное имя класса — это неквалифицированное имя класса в пределах области видимости этого класса.
В class template , injected-class-name может использоваться либо как имя шаблона, ссылающееся на текущий шаблон, либо как имя класса, ссылающееся на текущую инстанциацию.
Содержание |
Объяснение
В области класса имя текущего класса или имя шаблона текущего шаблона класса рассматривается как если бы оно было именем публичного члена; это называется injected-class-name . Точка объявления имени находится непосредственно после открывающей фигурной скобки определения (шаблона) класса.
int X; struct X { void f() { X* p; // OK, X является injected-class-name ::X* q; // Ошибка: поиск имени находит имя переменной, которое скрывает имя структуры } }; template<class T> struct Y { void g() { Y* p; // OK, Y является injected-class-name Y<T>* q; // OK, Y является injected-class-name, но Y<T> не является } };
Как и другие члены, injected-class-names наследуются. При наличии private или protected наследования injected-class-name косвенного базового класса может оказаться недоступным в производном классе.
struct A {}; struct B : private A {}; struct C : public B { A* p; // Ошибка: injected-class-name A недоступен ::A* q; // OK, не использует injected-class-name {;
В шаблоне класса
Внедренное имя класса шаблона класса может использоваться как имя шаблона или имя типа.
В следующих случаях injected-class-name рассматривается как template-name самого шаблона класса:
-
За ним следует
<. - Используется как template template argument .
- Является конечным идентификатором в elaborated class specifier объявления friend class template.
В противном случае оно трактуется как имя типа и эквивалентно имени шаблона, за которым следуют параметры шаблона класса, заключённые в
<>
.
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 specialization
или
partial specialization
, когда внедренное имя класса используется как имя типа, оно эквивалентно имени шаблона, за которым следуют аргументы шаблона специализации или частичной специализации шаблона класса, заключенные в
<>
.
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: аргумент по умолчанию использует injected-class-name как шаблон
Поиск, который находит внедренное имя класса, может привести к неоднозначности в определенных случаях (например, если оно найдено в более чем одном базовом классе). Если все найденные внедренные имена классов ссылаются на специализации одного и того же шаблона класса, и если имя используется как имя шаблона, ссылка относится к самому шаблону класса, а не к его специализации, и не является неоднозначной.
template<class T> struct Base {}; template<class T> struct Derived: Base<int>, Base<char> { typename Derived::Base b; // ошибка: неоднозначность typename Derived::Base<double> d; // OK };
Внедренное имя класса и конструкторы
Конструкторы не имеют имён, но внедрённое-имя-класса (injected-class-name) охватывающего класса считается именем конструктора в объявлениях и определениях конструкторов.
В квалифицированном имени
C::D
, если
- поиск по имени не игнорирует имена функций, и
-
поиск
Dв области видимости классаCнаходит его injected-class-name
квалифицированное имя всегда считается именующим конструктор
C
. Такое имя может использоваться только в объявлении конструктора (например, в объявлении friend-конструктора, специализации шаблона конструктора, инстанциации шаблона конструктора или определении конструктора)
или для наследования конструкторов
(since 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++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 1004 | C++98 |
injected-class-name не мог быть
template template argument |
разрешено, в этом случае он ссылается на сам
class template |
| CWG 2637 | C++98 | весь template-id мог быть injected-class-name | только template name может |