17 Templates [temp]

17.7 Template instantiation and specialization [temp.spec]

Акт создания экземпляра функции, класса, члена шаблона класса или шаблона члена называется template instantiation.

Функция, созданная из шаблона функции, называется созданной функцией. Класс, созданный из шаблона класса, называется созданным классом. Функция-член, класс-член, перечисление членов или статический член данных шаблона класса, созданный из определения члена шаблона класса, вызывается, соответственно, созданной функцией-членом, классом-членом, перечислением члена или статическим членом-данными . Функция-член, созданная из шаблона функции-члена, называется созданной функцией-членом. Класс-член, созданный из шаблона класса-члена, называется созданным экземпляром класса-члена.

Явная специализация может быть объявлена ​​для шаблона функции, шаблона класса, члена шаблона класса или шаблона члена. Явное объявление специализации вводится template<>. В явном объявлении специализации для шаблона класса, члена шаблона класса или шаблона члена класса имя явно специализированного класса должно быть simple-template-id. В явном объявлении специализации для шаблона функции или шаблона функции-члена имя явно специализированной функции или функции-члена может быть template-id. [Example:

template<class T = int> struct A {
  static int x;
};
template<class U> void g(U) { }

template<> struct A<double> { };        // specialize for T == double
template<> struct A<> { };              // specialize for T == int
template<> void g(char) { }             // specialize for U == char
                                        // U is deduced from the parameter type
template<> void g<int>(int) { }         // specialize for U == int
template<> int A<char>::x = 0;          // specialize for T == char

template<class T = int> struct B {
  static int x;
};
template<> int B<>::x = 1;              // specialize for T == int

end example]

Созданный экземпляр специализации шаблона может быть либо implicitly instantiated для данного списка аргументов, либо быть explicitly instantiated. Специализация - это класс, функция или член класса, который либо создается, либо explicitly specialized.

Для данного шаблона и данного набора template-arguments,

  • явное определение экземпляра должно появляться в программе не более одного раза,

  • явная специализация должна быть определена в программе не более одного раза (согласно [basic.def.odr]), и

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

Для диагностики нарушения этого правила реализация не требуется.

Каждая специализация шаблона класса, созданная из шаблона, имеет свою собственную копию любых статических членов. [Example:

template<class T> class X {
  static T s;
};
template<class T> T X<T>::s = 0;
X<int> aa;
X<char*> bb;

X<int> имеет статический член s типа int и X<char*> имеет статический член s типа char*. ]end example

Если объявление функции приобрело свой тип функции через dependent type без использования синтаксической формы декларатора функции, программа имеет неправильный формат. [Example:

template<class T> struct A {
  static T t;
};
typedef int function();
A<function> a;      // ill-formed: would declare A<function>​::​t as a static member function

end example]

17.7.1 Implicit instantiation [temp.inst]

Если специализация шаблона класса не была explicitly instantiated или explicitly specialized, специализация шаблона класса неявно создается, когда на специализацию ссылаются в контексте, который требует полностью определенного типа объекта, или когда полнота типа класса влияет на семантику программы. [ Note: В частности, если семантика выражения зависит от списков членов или базовых классов специализации шаблона класса, специализация шаблона класса генерируется неявно. Например, удаление указателя на тип класса зависит от того, объявляет ли класс деструктор, а преобразование между указателями в тип класса зависит от отношения наследования между двумя задействованными классами. ] [end noteExample:

template<class T> class B { /* ... */ };
template<class T> class D : public B<T> { /* ... */ };

void f(void*);
void f(B<int>*);

void g(D<int>* p, D<char>* pp, D<double>* ppp) {
  f(p);             // instantiation of D<int> required: call f(B<int>*)
  B<char>* q = pp;  // instantiation of D<char> required: convert D<char>* to B<char>*
  delete ppp;       // instantiation of D<double> required
}

end example] Если шаблон класса был объявлен, но не определен, при point of instantiationсоздании экземпляра будет получен неполный тип класса ([basic.types]). [Example:

template<class T> class X;
X<char> ch;         // error: incomplete type X<char>

end example] [ Note: В объявлении шаблона local class перечисление или и члены локального класса никогда не считаются сущностями, которые могут быть созданы отдельно (это включает их аргументы по умолчанию noexcept-specifiers, и инициализаторы нестатических элементов данных, если таковые имеются). В результате выполняется поиск зависимых имен, проверяются семантические ограничения, и любые используемые шаблоны создаются как часть создания экземпляра объекта, в котором объявлен локальный класс или перечисление. ]end note

Неявное создание экземпляра специализации шаблона класса вызывает неявное создание экземпляров объявлений, но не определений, аргументов по умолчанию или noexcept-specifiers функций-членов класса, классов-членов, перечислений элементов с заданной областью действия, элементов статических данных, шаблонов элементов и других элементов; и это вызывает неявное создание экземпляров определений перечислений членов с незаданной областью и анонимных объединений членов. Однако для определения того, является ли созданное повторное объявление действительным в соответствии с [basic.def.odr] и [class.mem], объявление, которое соответствует определению в шаблоне, считается определением. [Example:

template<class T, class U>
struct Outer {
  template<class X, class Y> struct Inner;
  template<class Y> struct Inner<T, Y>;         // #1a
  template<class Y> struct Inner<T, Y> { };     // #1b; OK: valid redeclaration of #1a
  template<class Y> struct Inner<U, Y> { };     // #2
};

Outer<int, int> outer;                          // error at #2

Outer<int, int>​::​Inner<int, Y> повторно заявлен на # 1b. (Он не определен, но отмечен как связанный с определением в Outer<T, U>.) # 2 также является повторным объявлением # 1a. Он отмечен как связанный с определением, поэтому это недопустимое повторное объявление той же частичной специализации.

template<typename T> struct Friendly {
  template<typename U> friend int f(U) { return sizeof(T); }
};
Friendly<char> fc;
Friendly<float> ff;                             // ill-formed: produces second definition of f(U)

end example]

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

Если специализация шаблона функции не была явно создана или явно специализирована, специализация шаблона функции создается неявно, когда на специализацию ссылаются в контексте, который требует существования определения функции. Функция, объявление которой было создано из определения дружественной функции, создается неявно, когда на нее ссылаются в контексте, который требует существования определения функции. Если вызов не относится к явной специализации шаблона функции или к функции-члену явно специализированного шаблона класса, аргумент по умолчанию для шаблона функции или функция-член шаблона класса неявно создается, когда функция вызывается в контексте, который требует значение аргумента по умолчанию.

[Example:

template<class T> struct Z {
  void f();
  void g();
};

void h() {
  Z<int> a;         // instantiation of class Z<int> required
  Z<char>* p;       // instantiation of class Z<char> not required
  Z<double>* q;     // instantiation of class Z<double> not required

  a.f();            // instantiation of Z<int>​::​f() required
  p->g();           // instantiation of class Z<char> required, and
                    // instantiation of Z<char>​::​g() required
}

Ничто в этом примере не требуется class Z<double>, Z<int>​::​g()или Z<char>​::​f() неявный экземпляр. ]end example

Если специализация шаблона переменной не была явно создана или явно специализирована, специализация шаблона переменной создается неявно при использовании этой специализации. Аргумент шаблона по умолчанию для шаблона переменной неявно создается, когда шаблон переменной упоминается в контексте, который требует значения аргумента по умолчанию.

Если функция, выбранная с помощью, overload resolution может быть определена без создания экземпляра определения шаблона класса, не указано, действительно ли это создание имеет место. [Example:

template <class T> struct S {
  operator int();
};

void f(int);
void f(S<int>&);
void f(S<float>);

void g(S<int>& sr) {
  f(sr);            // instantiation of S<int> allowed but not required
                    // instantiation of S<float> allowed but not required
};

end example]

Если шаблон функции или специализация шаблона функции-члена используется способом, который включает разрешение перегрузки, объявление специализации неявно создается ([temp.over]).

Реализация не должна неявно создавать экземпляр шаблона функции, шаблона переменной, шаблона члена, невиртуальной функции-члена, класса-члена, члена статических данных в шаблоне класса или подвыпуска constexpr if оператора, если только такое создание не требуется. . Не указано, создает ли реализация неявно виртуальную функцию-член шаблона класса, если в противном случае виртуальная функция-член не была бы создана. Использование специализации шаблона в аргументе по умолчанию не должно вызывать неявное создание экземпляра шаблона, за исключением того, что может быть создан экземпляр шаблона класса, где его полный тип необходим для определения правильности аргумента по умолчанию. Использование аргумента по умолчанию в вызове функции вызывает неявное создание экземпляров специализаций в аргументе по умолчанию.

Неявно созданные экземпляры специализации шаблонов классов, функций и переменных помещаются в пространство имен, в котором определен шаблон. Неявно созданные экземпляры специализаций для членов шаблона класса помещаются в пространство имен, где определен включающий шаблон класса. Неявно созданные экземпляры шаблонов членов помещаются в пространство имен, в котором определен включающий класс или шаблон класса. [Example:

namespace N {
  template<class T> class List {
  public:
    T* get();
  };
}

template<class K, class V> class Map {
public:
  N::List<V> lt;
  V get(K);
};

void g(Map<const char*,int>& m) {
  int i = m.get("Nicholas");
}

вызов lt.get() from Map<const char*,int>​::​get() будет помещаться List<int>​::​get() в пространство имен, N а не в глобальное пространство имен. ]end example

Если шаблон функции f вызывается способом, требующим использования аргумента по умолчанию, выполняется поиск зависимых имен, проверяются ограничения семантики, и создание экземпляра любого шаблона, используемого в аргументе по умолчанию, выполняется, как если бы аргумент по умолчанию имел был инициализатором, используемым в специализации шаблона функции с той же областью действия, теми же параметрами шаблона и тем же доступом, что и у шаблона функции, f используемого в этой точке, за исключением того, что область, в которой объявлен тип закрытия ([expr.prim.lambda.closure]) - и, следовательно, ее связанные пространства имен - остаются такими, как определено из контекста определения для аргумента по умолчанию. Этот анализ называется default argument instantiation. Созданный аргумент по умолчанию затем используется как аргумент f.

Каждый аргумент по умолчанию создается независимо. [Example:

template<class T> void f(T x, T y = ydef(T()), T z = zdef(T()));

class  A { };

A zdef(A);

void g(A a, A b, A c) {
  f(a, b, c);       // no default argument instantiation
  f(a, b);          // default argument z = zdef(T()) instantiated
  f(a);             // ill-formed; ydef is not declared
}

end example]

Специализация noexcept-specifierшаблона функции не создается вместе с объявлением функции; он создается при необходимости ([except.spec]). Если такой объект noexcept-specifierнеобходим, но еще не создан, выполняется поиск зависимых имен, проверяются ограничения семантики, и создание экземпляра любого шаблона, используемого в, noexcept-specifierвыполняется так, как если бы оно было выполнено как часть создания экземпляра объявления объекта. специализация на этом этапе.

[ Note: [temp.point] определяет точку реализации специализации шаблона. ]end note

Существует количество, определяемое реализацией, которое указывает предел общей глубины рекурсивных экземпляров ([implimits]), которые могут включать более одного шаблона. Результат бесконечной рекурсии при создании экземпляра не определен. [Example:

template<class T> class X {
  X<T>* p;          // OK
  X<T*> a;          // implicit generation of X<T> requires
                    // the implicit instantiation of X<T*> which requires
                    // the implicit instantiation of X<T**> which …
};

end example]

17.7.2 Explicit instantiation [temp.explicit]

Специализация шаблона класса, функции, переменной или члена может быть явно создана из его шаблона. Функция-член, класс-член или статический член-данные шаблона класса могут быть явно созданы из определения члена, связанного с его шаблоном класса. Явное создание экземпляра шаблона функции или функции-члена шаблона класса не должно использовать спецификаторы inline или constexpr .

Синтаксис явного создания экземпляра:

explicit-instantiation:
	externopt template declaration

Есть две формы явного создания экземпляра: явное определение экземпляра и явное объявление экземпляра. Явное объявление экземпляра начинается с extern ключевого слова.

Если явная Инстанциация для класса или члена класса, elaborated-type-specifierв declarationдолжны включать в себя simple-template-id. Если явная Инстанциация для функции или члена функции, то unqualified-id в declaration должна быть либо template-id или, когда все аргументы шаблона может быть выведены, template-nameили operator-function-id. [ Note: Декларация может объявить qualified-id, в этом случае unqualified-id из qualified-id должно быть template-id. ] Если явное создание экземпляра предназначено для функции-члена, класса-члена или статического члена данных специализации шаблона класса, имя специализации шаблона класса в для имени члена должно быть . Если явное создание экземпляра предназначено для переменной, в объявлении должен быть . Явное создание экземпляра должно появиться во включающем пространстве имен своего шаблона. Если имя, объявленное в явном создании экземпляра, является неквалифицированным именем, явное создание экземпляра должно появиться в пространстве имен, в котором объявлен его шаблон, или, если это пространство имен является inline ( ), в любом пространстве имен из включающего его набора пространств имен. [ Относительно полных имен в деклараторах см . ] [end notequalified-idsimple-template-idunqualified-idtemplate-id[namespace.def]Note: [dcl.meaning]end noteExample:

template<class T> class Array { void mf(); };
template class Array<char>;
template void Array<int>::mf();

template<class T> void sort(Array<T>& v) { /* ... */ }
template void sort(Array<char>&);       // argument is deduced here

namespace N {
  template<class T> void f(T&) { }
}
template void N::f<int>(int&);

end example]

Объявление шаблона функции, шаблона переменной, функции-члена или статического члена данных шаблона класса или шаблона функции-члена класса или шаблона класса должно предшествовать явному созданию экземпляра этой сущности. Определение шаблона класса, класса-члена шаблона класса или шаблона класса-члена шаблона класса или класса должно предшествовать явному созданию экземпляра этой сущности, если только явному созданию экземпляра не предшествует явная специализация сущности с тем же аргументы шаблона. Если declaration явное создание именует неявно объявленное special member function, программа имеет неправильный формат .

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

Явное создание экземпляра класса, шаблона функции или специализации шаблона переменной помещается в пространство имен, в котором определен шаблон. Явное создание экземпляра члена шаблона класса помещается в пространство имен, в котором определен включающий шаблон класса. Явное создание экземпляра шаблона члена помещается в пространство имен, в котором определен включающий класс или шаблон класса. [Example:

namespace N {
  template<class T> class Y { void mf() { } };
}

template class Y<int>;          // error: class template Y not visible in the global namespace

using N::Y;
template class Y<int>;          // error: explicit instantiation outside of the namespace of the template

template class N::Y<char*>;             // OK: explicit instantiation in namespace N
template void N::Y<double>::mf();       // OK: explicit instantiation in namespace N

end example]

Конечный конец template-argument можно оставить неопределенным при явном создании экземпляра специализации шаблона функции или специализации шаблона функции-члена при условии, что он может быть выведен из типа параметра функции ([temp.deduct]). [Example:

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }

// instantiate sort(Array<int>&) – template-argument deduced
template void sort<>(Array<int>&);

end example]

Явное создание экземпляра, которое именует специализацию шаблона класса, также является явным экземпляром того же типа (объявление или определение) каждого из его членов (не включая элементы, унаследованные от базовых классов, и элементы, которые являются шаблонами), который ранее не был явно специализирован в единица трансляции, содержащая явное создание экземпляра, за исключением случаев, описанных ниже. [ Note: Кроме того, это обычно будет явным экземпляром определенных данных о классе, зависящих от реализации. ] end note

Явное определение экземпляра, которое именует специализацию шаблона класса, явно создает экземпляр специализации шаблона класса и является явным определением экземпляра только тех членов, которые были определены в точке создания экземпляра.

За исключением встроенных функций и переменных, объявления с типами, выведенными из их инициализатора или возвращаемого значения ([dcl.spec.auto]), const переменных литеральных типов, переменных ссылочных типов и специализаций шаблонов классов, явные объявления создания экземпляров имеют эффект подавления неявного создания экземпляра объекта для на которые они ссылаются. [ Note: Намерение состоит в том, чтобы встроенная функция, являющаяся предметом явного объявления экземпляра, по-прежнему odr-used создавалась неявно, когда так, чтобы тело можно было рассматривать для встраивания, но чтобы внешняя копия встроенной функции не создавалась в единица перевода. ]end note

Если объект является предметом как явного объявления создания экземпляра, так и явного определения экземпляра в одной и той же единице преобразования, определение должно следовать за объявлением. Сущность, которая является предметом явного объявления экземпляра и который также используется таким образом, который в противном случае вызвал бы implicit instantiation в блоке преобразования, должен быть предметом явного определения экземпляра где-то в программе; в противном случае программа имеет неправильный формат, и диагностика не требуется. [ Note: Это правило применяется к встроенным функциям, даже если явное объявление экземпляра такой сущности не имеет другого нормативного эффекта. Это необходимо для того, чтобы гарантировать, что если адрес встроенной функции будет взят в блоке трансляции, в котором реализация решила подавить внешнее тело, другая единица трансляции предоставит тело. ] Явное объявление экземпляра не должно называть специализацию шаблона с внутренней связью.end note

Обычные правила проверки доступа не применяются к именам, используемым для указания явных экземпляров. [ Note: В частности, аргументы и имена шаблона, используемые в деклараторе функции (включая типы параметров, возвращаемые типы и спецификации исключений), могут быть частными типами или объектами, которые обычно недоступны, а шаблон может быть шаблоном члена или функцией-членом, которая будет обычно недоступны. ]end note

Явное создание экземпляра не означает использование аргумента по умолчанию, поэтому создание экземпляра аргумента по умолчанию не выполняется. [Example:

char* p = 0;
template<class T> T g(T x = &p) { return x; }
template int g<int>(int);       // OK even though &p isn't an int.

end example]

17.7.3 Explicit specialization [temp.expl.spec]

Явная специализация любого из следующего:

  • шаблон функции

  • шаблон класса

  • шаблон переменных

  • функция-член шаблона класса

  • статический член данных шаблона класса

  • класс-член шаблона класса

  • перечисление членов шаблона класса

  • шаблон класса члена класса или шаблона класса

  • шаблон функции-члена класса или шаблона класса

может быть объявлено посредством объявления, представленного template<>; то есть:

explicit-specialization:
	template < > declaration

[Example:

template<class T> class stream;

template<> class stream<char> { /* ... */ };

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }

template<> void sort<char*>(Array<char*>&);

Учитывая эти объявления, stream<char> будет использоваться как определение потоков chars; другие потоки будут обрабатываться специализациями шаблонов классов, созданными из шаблона класса. Точно так же sort<char*> будет использоваться функция сортировки для аргументов типа Array<char*>; другие Array типы будут отсортированы по функциям, сгенерированным из шаблона. ]end example

Явная специализация может быть объявлена в любой области , в которой может быть определен соответствующий первичный шаблон ([namespace.memdef], [class.mem], [temp.mem]).

Объявление явной специализации шаблона функции, шаблона класса или шаблона переменной должно предшествовать объявлению явной специализации. [ Note: Требуется декларация, но не определение шаблона. ] Определение класса или шаблона класса должно предшествовать объявлению явной специализации для шаблона члена этого класса или шаблона класса. [end noteExample:

template<> class X<int> { /* ... */ };          // error: X not a template

template<class T> class X;

template<> class X<char*> { /* ... */ };        // OK: X is a template

end example]

Функция-член, шаблон функции-члена, класс-член, перечисление члена, шаблон класса-члена, статический член-данные или статический элемент-данные-шаблон шаблона класса могут быть явно специализированы для неявно создаваемой специализации класса; в этом случае определение шаблона класса должно предшествовать явной специализации для члена шаблона класса. Если такая явная специализация для члена шаблона класса называет неявно объявленную специальную функцию-член (предложение [special]), программа плохо сформирована.

Член явно специализированного класса не создается неявно из объявления члена шаблона класса; вместо этого член специализации шаблона класса должен быть явно определен, если требуется его определение. В этом случае определение явной специализации шаблона класса должно входить в область видимости в точке, в которой определяется член. Определение явно специализированного класса не связано с определением сгенерированной специализации. То есть его члены не обязательно должны иметь те же имена, типы и т. Д., Что и члены сгенерированной специализации. Члены явно специализированного шаблона класса определяются так же, как члены обычных классов, и не используют template<> синтаксис. То же самое верно и при определении члена явно специализированного класса-члена. Однако template<> он используется при определении члена явно специализированного шаблона класса члена, который специализирован как шаблон класса. [Example:

template<class T> struct A {
  struct B { };
  template<class U> struct C { };
};

template<> struct A<int> {
  void f(int);
};

void h() {
  A<int> a;
  a.f(16);          // A<int>​::​f must be defined somewhere
}

// template<> not used for a member of an explicitly specialized class template
void A<int>::f(int) { /* ... */ }

template<> struct A<char>::B {
  void f();
};
// template<> also not used when defining a member of an explicitly specialized member class
void A<char>::B::f() { /* ... */ }

template<> template<class U> struct A<char>::C {
  void f();
};
// template<> is used when defining a member of an explicitly specialized member class template
// specialized as a class template
template<>
template<class U> void A<char>::C<U>::f() { /* ... */ }

template<> struct A<short>::B {
  void f();
};
template<> void A<short>::B::f() { /* ... */ }              // error: template<> not permitted

template<> template<class U> struct A<short>::C {
  void f();
};
template<class U> void A<short>::C<U>::f() { /* ... */ }    // error: template<> required

end example]

Если шаблон, шаблон члена или член шаблона класса явно специализирован, тогда эта специализация должна быть объявлена ​​перед первым использованием этой специализации, которая вызовет неявное создание экземпляра, в каждой единице перевода, в которой происходит такое использование. ; Диагностика не требуется. Если программа не предоставляет определение для явной специализации и либо специализация используется таким образом, что может иметь место неявное создание экземпляра, либо член является виртуальной функцией-членом, программа имеет неправильный формат, диагностика не требуется. Неявное создание экземпляра никогда не создается для явной специализации, которая объявлена, но не определена. [Example:

class String { };
template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }

void f(Array<String>& v) {
  sort(v);          // use primary template sort(Array<T>&), T is String
}

template<> void sort<String>(Array<String>& v);     // error: specialization after use of primary template
template<> void sort<>(Array<char*>& v);            // OK: sort<char*> not yet used
template<class T> struct A {
  enum E : T;
  enum class S : T;
};
template<> enum A<int>::E : int { eint };           // OK
template<> enum class A<int>::S : int { sint };     // OK
template<class T> enum A<T>::E : T { eT };
template<class T> enum class A<T>::S : T { sT };
template<> enum A<char>::E : char { echar };        // ill-formed, A<char>​::​E was instantiated
                                                    // when A<char> was instantiated
template<> enum class A<char>::S : char { schar };  // OK

end example]

Размещение явных объявлений специализации для шаблонов функций, шаблонов классов, шаблонов переменных, функций-членов шаблонов классов, статических элементов данных шаблонов классов, классов-членов шаблонов классов, перечислений элементов шаблонов классов, шаблонов классов-членов шаблонов классов, функций-членов шаблоны шаблонов классов, шаблоны элементов статических данных шаблонов классов, функции-члены шаблонов-членов шаблонов классов, функции-члены шаблонов-членов нешаблонных классов, шаблоны элементов статических данных нешаблонных классов, шаблоны функций-членов классов-членов шаблоны классов и т. д., а также размещение объявлений частичной специализации шаблонов классов, шаблонов переменных, шаблонов классов-членов нешаблонных классов, статических шаблонов элементов данных нешаблонных классов, шаблонов классов-членов шаблонов классов и т. д. влияет на то, правильно ли сформирована программа в соответствии с относительным расположением явной спецификации lization и их точки создания в блоке перевода, как указано выше и ниже. При написании специализации внимательно относитесь к ее расположению; или заставить его скомпилировать будет такое испытание, чтобы разжечь его самосожжение.

Явная специализация шаблона находится в области пространства имен, в котором был определен шаблон. [Example:

namespace N {
  template<class T> class X { /* ... */ };
  template<class T> class Y { /* ... */ };

  template<> class X<int> { /* ... */ };        // OK: specialization in same namespace
  template<> class Y<double>;                   // forward-declare intent to specialize for double
}

template<> class N::Y<double> { /* ... */ };    // OK: specialization in enclosing namespace
template<> class N::Y<short> { /* ... */ };     // OK: specialization in enclosing namespace

end example]

Объект, simple-template-id который называет явную специализацию шаблона класса, которая была объявлена, но не определена, может использоваться точно так же, как имена других не полностью определенных классов ([basic.types]). [Example:

template<class T> class X;                      // X is a class template
template<> class X<int>;

X<int>* p;                                      // OK: pointer to declared class X<int>
X<int> x;                                       // error: object of incomplete class X<int>

end example]

Конечный конец template-argument можно не указывать в template-id явном названии специализации шаблона функции, при условии, что он может быть выведен из типа аргумента функции. [Example:

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v);

// explicit specialization for sort(Array<int>&)
// with deduced template-argument of type int
template<> void sort(Array<int>&);

end example]

Функция с тем же именем, что и шаблон, и типом, который точно соответствует специализации шаблона, не является явной специализацией ([temp.fct]).

Явная специализация шаблона функции или переменной является встроенной, только если она объявлена ​​со inline спецификатором или определена как удаленная, и независимо от того, является ли ее шаблон функции или переменной встроенным. [Example:

template<class T> void f(T) { /* ... */ }
template<class T> inline T g(T) { /* ... */ }

template<> inline void f<>(int) { /* ... */ }   // OK: inline
template<> int g<>(int) { /* ... */ }           // OK: not inline

end example]

Явная специализация статического элемента данных шаблона или явная специализация шаблона статического элемента данных является определением, если объявление включает инициализатор; в противном случае это декларация. [ Note: Определение статического элемента данных шаблона, который требует инициализации по умолчанию, должно использовать braced-init-list:

template<> X Q<int>::x;                         // declaration
template<> X Q<int>::x ();                      // error: declares a function
template<> X Q<int>::x { };                     // definition

end note]

Член или шаблон члена шаблона класса может быть явно специализирован для данного неявного создания экземпляра шаблона класса, даже если член или шаблон члена определен в определении шаблона класса. Явная специализация элемента или шаблона элемента указывается с использованием синтаксиса явной специализации. [Example:

template<class T> struct A {
  void f(T);
  template<class X1> void g1(T, X1);
  template<class X2> void g2(T, X2);
  void h(T) { }
};

// specialization
template<> void A<int>::f(int);

// out of class member template definition
template<class T> template<class X1> void A<T>::g1(T, X1) { }

// member template specialization
template<> template<class X1> void A<int>::g1(int, X1);

// member template specialization
template<> template<>
  void A<int>::g1(int, char);           // X1 deduced as char
template<> template<>
  void A<int>::g2<char>(int, char);     // X2 specified as char

// member specialization even if defined in class definition
template<> void A<int>::h(int) { }

end example]

Член или шаблон члена могут быть вложены во многие шаблоны включающих классов. При явной специализации для такого члена объявлению члена должен предшествовать a template<> для каждого шаблона включающего класса, который явно специализирован. [Example:

template<class T1> class A {
  template<class T2> class B {
    void mf();
  };
};
template<> template<> class A<int>::B<double>;
template<> template<> void A<char>::B<char>::mf();

end example]

В явном объявлении специализации для члена шаблона класса или шаблона члена, который появляется в области пространства имен, шаблон члена и некоторые из включающих его шаблонов классов могут оставаться неспециализированными, за исключением того, что объявление не должно явно специализировать шаблон члена класса, если его шаблоны включающих классов также не являются явно специализированными. В таком явном объявлении специализации должно быть предоставлено ключевое слово, template за которым следует a template-parameter-list, вместо template<> предшествующего явного объявления специализации члена. Типы template-parameters в template-parameter-list должны быть такими же, как те, которые указаны в определении первичного шаблона. [Example:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};
template <> template <class X>
  class A<int>::B {
      template <class T> void mf1(T);
  };
template <> template <> template<class T>
  void A<int>::B<double>::mf1(T t) { }
template <class Y> template <>
  void A<Y>::B<double>::mf2() { }       // ill-formed; B<double> is specialized but
                                        // its enclosing class template A is not

end example]

Специализация шаблона функции-члена, шаблона класса-члена или шаблона статических данных-члена неспециализированного шаблона класса сама по себе является шаблоном.

Явное объявление специализации не должно быть дружественным объявлением.

Аргументы функции по умолчанию не должны указываться в объявлении или определении для одной из следующих явных специализаций:

  • явная специализация шаблона функции;

  • явная специализация шаблона функции-члена;

  • явная специализация функции-члена шаблона класса, где неявно создается экземпляр специализации шаблона класса, к которой принадлежит специализация функции-члена. [ Note: Аргументы функции по умолчанию могут быть указаны в объявлении или определении функции-члена специализации шаблона класса, которая является явно специализированной. ] end note