17 Templates [temp]

A template определяет семейство классов, функций или переменных или псевдоним для семейства типов.

template-declaration:
	template < template-parameter-list > declaration
template-parameter-list:
	template-parameter
	template-parameter-list , template-parameter

[ Маркер следующий за оператором А может быть продукт заменяя маркер на два следующих друг за другом маркеров ( ). ]Note: > template-parameter-listtemplate-declaration>> >[temp.names]end note

declaration В template-declaration буду

  • объявить или определить функцию, класс или переменную, или

  • определить функцию-член, класс-член, перечисление членов или статический член данных шаблона класса или класса, вложенного в шаблон класса, или

  • определить шаблон члена класса или шаблона класса, или

  • быть deduction-guide, или

  • быть alias-declaration.

А template-declaration- это declaration. A template-declarationтакже является определением, если оно declarationопределяет функцию, класс, переменную или статический член данных. Объявление, представленное шаблоном объявления переменной, - это файл variable template. Шаблон переменной в области класса - это static data member template.

[Example:

template<class T>
  constexpr T pi = T(3.1415926535897932385L);
template<class T>
T circular_area(T r) {
  return pi<T> * r * r;
}
struct matrix_constants {
  template<class T>
   using pauli = hermitian_matrix<T, 2>;
  template<class T>
   constexpr pauli<T> sigma1 = { { 0, 1 }, { 1, 0 } };
  template<class T>
   constexpr pauli<T> sigma2 = { { 0, -1i }, { 1i, 0 } };
  template<class T>
   constexpr pauli<T> sigma3 = { { 1, 0 }, { 0, -1 } };
};

end example]

A template-declaration может отображаться только как область пространства имен или объявление области действия класса. В объявлении шаблона функции последний компонент declarator-id не должен быть template-id. [ Note: Этот последний компонент может быть a identifier, an operator-function-id, a conversion-function-idили a literal-operator-id. В объявлении шаблона класса, если имя класса - a simple-template-id, объявление объявляет class template partial specialization. ]end note

В template-declarationявной специализации или явном создании экземпляра объект init-declarator-list в объявлении должен содержать не более одного декларатора. Когда такое объявление используется для объявления шаблона класса, использование декларатора не допускается.

Название шаблона имеет linkage. Специализации (явные или неявные) шаблона, имеющего внутреннюю связь, отличаются от всех специализаций в других единицах перевода. Шаблон, шаблон explicit specializationи частичная специализация шаблона класса не должны иметь связи C. Использование спецификации связывания, отличной от любой из этих конструкций "C" или "C++" с любой из этих конструкций, поддерживается условно с семантикой, определяемой реализацией. Определения шаблонов должны соответствовать требованиям one-definition rule. [ Note: Аргументы по умолчанию для шаблонов функций и для функций-членов шаблонов классов считаются определениями для целей создания экземпляров шаблона ( [temp.decls]) и также должны подчиняться правилу одного определения. ]end note

Шаблон класса не должен иметь то же имя, что и любой другой шаблон, класс, функция, переменная, перечисление, перечислитель, пространство имен или тип в той же области ( [basic.scope]), за исключением случаев, указанных в [temp.class.spec]. За исключением того, что шаблон функции может быть перегружен либо нешаблонными функциями ( [dcl.fct]) с тем же именем, либо другими шаблонами функций с тем же именем ( [temp.over]), имя шаблона, объявленное в области пространства имен или в области класса, должно быть уникальным в этой области.

А templated entity это

  • шаблон,

  • сущность, определенная ( [basic.def]) или созданная ( [class.temporary]) в шаблонной сущности,

  • член шаблонной сущности,

  • перечислитель для перечисления, который является шаблонным объектом, или

  • тип закрытия a lambda-expression( [expr.prim.lambda.closure]), появляющийся в объявлении шаблонной сущности.

[ Note: Локальный класс, локальная переменная или дружественная функция, определенные в шаблонной сущности, являются шаблонной сущностью. ]end note

Шаблон функции, функция-член шаблона класса, шаблон переменной или статический член данных шаблона класса должны быть определены в каждой единице трансляции, в которой он находится, implicitly instantiated если соответствующая специализация не находится explicitly instantiated в какой-либо единице трансляции; Диагностика не требуется.

17.1 Template parameters [temp.param]

Синтаксис для template-parameters :

template-parameter:
	type-parameter
	parameter-declaration
type-parameter:
	type-parameter-key ...opt identifieropt
	type-parameter-key identifieropt = type-id
	template < template-parameter-list > type-parameter-key ...opt identifieropt
	template < template-parameter-list > type-parameter-key identifieropt = id-expression
type-parameter-key:
	class
	typename

[ Маркер следующий за оператором А может быть продукт заменяя маркер на два следующих друг за другом маркеров ( ). ]Note: > template-parameter-listtype-parameter>> >[temp.names]end note

Нет смысловой разницы между class и typename в type-parameter-key. typename за которым следует unqualified-id имя параметра типа шаблона. typename за которым следует a, qualified-id обозначает тип, не являющийся типом . Форма A - это . [135 parameter-declarationtemplate-parameterclass identifiertype-parameterExample:

class T { /* ... */ };
int i;

template<class T, T i> void f(T t) {
  T t1 = i;         // template-parameters T and i
  ::T t2 = ::i;     // global namespace members T and i
}

Здесь шаблон f имеет type-parameter вызываемый T, а не безымянный не тип template-parameterкласса T. ] Класс хранения не должен указываться в декларации. Типы не должны определяться в декларации.end exampletemplate-parametertemplate-parameter

A type-parameter , идентификатор которого не следует за многоточием, определяет его identifier как typedef-name (если он объявлен без template) или template-name (если объявлен с template) в области действия объявления шаблона. [ Note: Аргумент шаблона может быть шаблоном класса или шаблоном псевдонима. Например,

template<class T> class myarray { /* ... */ };

template<class K, class V, template<class T> class C = myarray>
class Map {
  C<K> key;
  C<V> value;
};

end note]

Не-тип template-parameter должен иметь один из следующих (необязательно cv-квалифицированных) типов:

  • целочисленный или перечислимый тип,

  • указатель на объект или указатель на функцию,

  • lvalue ссылка на объект или lvalue ссылка на функцию,

  • указатель на член,

  • std​::​nullptr_­t, или

  • тип, содержащий placeholder type.

[ Note: Другие типы запрещены либо явно ниже, либо неявно правилами, регулирующими форму template-arguments ( [temp.arg]). ] Верхний уровень на игнорируется при определении его типа.end notecv-qualifierstemplate-parameter

template-parameter Отсутствие ссылки на тип - это prvalue. Его нельзя присвоить или каким-либо иным образом изменить его значение. template-parameter Адрес, не относящийся к типу, не может быть взят. Когда template-parameter в качестве инициализатора для ссылки используется не-типовая не-ссылка, всегда используется временная. [Example:

template<const X& x, int i> void f() {
  i++;                          // error: change of template-parameter value

  &x;                           // OK
  &i;                           // error: address of non-reference template-parameter

  int& ri = i;                  // error: non-const reference bound to temporary
  const int& cri = i;           // OK: const reference bound to temporary
}

end example]

Не-тип template-parameter нельзя объявлять как имеющий тип с плавающей запятой, класс или тип void. [Example:

template<double d> class X;     // error
template<double* pd> class Y;   // OK
template<double& rd> class Z;   // OK

end example]

Не тип типа «массив » или типа функции настраивается на тип «указатель на ». [template-parameter T T TExample:

template<int* a>   struct R { /* ... */ };
template<int b[5]> struct S { /* ... */ };
int p;
R<&p> w;                        // OK
S<&p> x;                        // OK due to parameter adjustment
int v[5];
R<v> y;                         // OK due to implicit argument conversion
S<v> z;                         // OK due to both adjustment and conversion

end example]

A default template-argument - это template-argument( [temp.arg]), указанный после = в a template-parameter. Значение по умолчанию template-argument может быть указано для любого типа template-parameter (тип, не тип, шаблон), кроме файла template parameter pack. Значение по умолчанию template-argument может быть указано в объявлении шаблона. Значение по умолчанию template-argument не должно указываться в template-parameter-lists определении члена шаблона класса, который появляется вне класса члена. Значение по умолчанию template-argument не должно указываться в объявлении шаблона дружественного класса. Если объявление шаблона функции друга указывает значение по умолчанию template-argument, это объявление должно быть определением и должно быть единственным объявлением шаблона функции в единице перевода.

Набор template-arguments доступных для использования значений по умолчанию получается путем объединения аргументов по умолчанию из всех предыдущих объявлений шаблона таким же образом, как аргументы функции по умолчанию ( [dcl.fct.default]). [Example:

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;

эквивалентно

template<class T1 = int, class T2 = int> class A;

end example]

Если template-parameter шаблон класса, шаблон переменной или шаблон псевдонима имеет значение по умолчанию template-argument, каждый последующий template-parameter должен либо иметь значение по умолчанию, template-argument либо быть пакетом параметров шаблона. Если a template-parameter из шаблона первичного класса, шаблона первичной переменной или шаблона псевдонима является пакетом параметров шаблона, он должен быть последним template-parameter. За пакетом параметров шаблона шаблона функции не должен следовать другой параметр шаблона, если только этот параметр шаблона не может быть выведен из списка-типа-параметра ( [dcl.fct]) шаблона функции или имеет аргумент по умолчанию ( [temp.deduct]). Параметр шаблона для шаблона руководства по вычету ( [temp.deduct.guide]), у которого нет аргумента по умолчанию, должен выводиться из списка типов-параметров шаблона руководства по вычету. [Example:

template<class T1 = int, class T2> class B;     // error

// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { }   // error
template<class... T, class U> void g() { }      // error

end example]

template-parameter Не дадутся по умолчанию аргументов два различных деклараций в том же объеме. [Example:

template<class T = int> class X;
template<class T = int> class X { /* ... */ };  // error

end example]

При синтаксическом разборе значения template-argument по умолчанию для не-типа template-parameterпервое невложенное значение > считается концом, template-parameter-list а не оператором "больше чем". [Example:

template<int i = 3 > 4 >        // syntax error
class X { /* ... */ };

template<int i = (3 > 4) >      // OK
class Y { /* ... */ };

end example]

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

template <class T = float> struct B {};
template <template <class TT = float> class T> struct A {
  inline void f();
  inline void g();
};
template <template <class TT> class T> void A<T>::f() {
  T<> t;            // error - TT has no default template argument
}
template <template <class TT = char> class T> void A<T>::g() {
    T<> t;          // OK - T<char>
}

end example]

Если a template-parameter- type-parameterс многоточием перед необязательным identifierили является a, parameter-declarationкоторый объявляет параметр pack ( [dcl.fct]), то template-parameter это a template parameter pack. Пакет параметров шаблона parameter-declaration, тип которого содержит один или несколько нерасширенных пакетов параметров, является расширением пакета. Аналогичным образом , параметр пакет шаблона , который является type-parameterс , template-parameter-listсодержащими один или более пакетами нерасширенных параметров является пакет расширения. Пакет параметров шаблона, который является расширением пакета, не должен расширять пакет параметров, объявленный в нем template-parameter-list. [Example:

template <class... Types> class Tuple;                // Types is a template type parameter pack
                                                      // but not a pack expansion
template <class T, int... Dims> struct multi_array;   // Dims is a non-type template parameter pack
                                                      // but not a pack expansion
template<class... T> struct value_holder {
  template<T... Values> struct apply { };             // Values is a non-type template parameter pack
                                                      // and a pack expansion
};
template<class... T, T... Values> struct static_array;// error: Values expands template type parameter
                                                      // pack T within the same template parameter list

end example]

Поскольку шаблон template-parameters и шаблон template-arguments рассматриваются как типы в описательных целях, термины non-type parameter и non-type argument используются для обозначения параметров и аргументов, не являющихся типом и шаблоном.

17.2 Names of template specializations [temp.names]

A template specialization может обозначаться как template-id:

simple-template-id:
	template-name < template-argument-listopt >
template-id:
	simple-template-id
	operator-function-id < template-argument-listopt >
	literal-operator-id < template-argument-listopt >
template-name:
	identifier
template-argument-list:
	template-argument ...opt
	template-argument-list , template-argument ...opt
template-argument:
	constant-expression
	type-id
	id-expression

[ Note: Эти name lookup правила используются , чтобы связать использование имени с декларацией шаблона; то есть, чтобы идентифицировать имя как template-name. ]end note

Чтобы a template-name было явно квалифицировано аргументами шаблона, необходимо, чтобы имя ссылалось на шаблон.

После того, как name lookup обнаруживается, что имя является a template-name или что a operator-function-idили a literal-operator-idотносится к набору перегруженных функций, любой член которых является шаблоном функции, если за ним следует a <, < всегда используется как разделитель a template-argument-list и никогда как менее- чем оператор. При синтаксическом анализе template-argument-listпервый невложенный >136 используется как конечный разделитель, а не как оператор «больше». Точно так же первый невложенный токен >> рассматривается как два последовательных, но различных > токена, первый из которых считается концом template-argument-listи завершает template-id. [ Note: Второй > токен, созданный этим правилом замены, может завершать включающую template-idконструкцию или может быть частью другой конструкции (например, приведение). ] [end noteExample:

template<int i> class X { /* ... */ };

X< 1>2 > x1;                            // syntax error
X<(1>2)> x2;                            // OK

template<class T> class Y { /* ... */ };
Y<X<1>> x3;                             // OK, same as Y<X<1> > x3;
Y<X<6>>1>> x4;                          // syntax error
Y<X<(6>>1)>> x5;                        // OK

end example]

template Говорят, что ключевое слово появляется на верхнем уровне в, qualified-id если оно появляется за пределами template-argument-listили decltype-specifier. В a qualified-idиз a declarator-idили в a, qualified-idобразованном символом class-head-nameили enum-head-name, ключевое слово template не должно появляться на верхнем уровне. В qualified-idкачестве имени в typename-specifier, elaborated-type-specifier, using-declaration, или class-or-decltype, необязательный ключевое слово template появляется на уровне верхней игнорируется. В этих контекстах < всегда предполагается , что токен представляет template-argument-list. Во всех других контекстах при именовании специализации шаблона элемента неизвестной специализации ( [temp.dep.type]) имя шаблона элемента должно начинаться с префикса ключевого слова template. [Example:

struct X {
  template<std::size_t> X* alloc();
  template<std::size_t> static X* adjust();
};
template<class T> void f(T* p) {
  T* p1 = p->alloc<200>();              // ill-formed: < means less than
  T* p2 = p->template alloc<200>();     // OK: < starts template argument list
  T::adjust<100>();                     // ill-formed: < means less than
  T::template adjust<100>();            // OK: < starts template argument list
}

end example]

Имя с префиксом ключевого слова template должно быть template-idили имя должно относиться к шаблону класса или шаблону псевдонима. [ Note: Ключевое слово template не может применяться к членам, не являющимся шаблонами, шаблонов классов. ] [ Как и в случае с префиксом, префикс разрешен в случаях, когда это не является строго необходимым; то есть, когда или выражение слева от или не зависит от , или использование не появляется в области действия шаблона. ] [end noteNote: typenametemplatenested-name-specifier->.template-parameterend noteExample:

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

template <class T> void f(T t) {
  A<T> a;
  a.template f<>(t);                    // OK: calls template
  a.template f(t);                      // error: not a template-id
}

template <class T> struct B {
  template <class T2> struct C { };
};

// OK: T​::​template C names a class template:
template <class T, template <class X> class TT = T::template C> struct D { };
D<B<int> > db;

end example]

А, simple-template-id который называет специализацию шаблона класса, является class-name.

А, template-idкоторый называет специализацию шаблона псевдонима, является type-name.

> , Покрывающие type-id из dynamic_­cast, static_­cast, reinterpret_­cast или const_­cast, или, вмещающих template-arguments в последующем template-id, считаются вложенным для целей настоящего описания.

17.3 Template arguments [temp.arg]

Существует три формы template-argument, соответствующие трем формам template-parameter: тип, нетип и шаблон. Тип и форма каждого из template-argument указанных в a template-id должны соответствовать типу и форме, указанным для соответствующего параметра, объявленного шаблоном в его template-parameter-list. Когда параметр, объявленный шаблоном, равен a template parameter pack, он будет соответствовать нулю или более template-arguments. [Example:

template<class T> class Array {
  T* v;
  int sz;
public:
  explicit Array(int);
  T& operator[](int);
  T& elem(int i) { return v[i]; }
};

Array<int> v1(20);
typedef std::complex<double> dcomplex;  // std​::​complex is a standard library template
Array<dcomplex> v2(30);
Array<dcomplex> v3(40);

void bar() {
  v1[3] = 7;
  v2[3] = v3.elem(4) = dcomplex(7,8);
}

end example]

В a template-argumentнеоднозначность между a type-id и выражением разрешается в a type-id, независимо от формы соответствующего template-parameter. 137 [Example:

template<class T> void f();
template<int I> void f();

void g() {
  f<int()>();       // int() is a type-id: call the first f()
}

end example]

Имя template-argument должно быть доступно в том месте, где оно используется как template-argument. [ Note: Если имя template-argument доступно в точке, где оно используется как template-argument, то дальнейшее ограничение доступа в результирующем экземпляре, где используется соответствующее template-parameter имя, не существует. ] [end noteExample:

template<class T> class X {
  static T t;
};

class Y {
private:
  struct S { /* ... */ };
  X<S> x;           // OK: S is accessible
                    // X<Y​::​S> has a static member of type Y​::​S
                    // OK: even though Y​::​S is private
};

X<Y::S> y;          // error: S not accessible

end example] Для a, template-argument который является типом класса или шаблоном класса, определение шаблона не имеет специальных прав доступа к членам template-argument. [Example:

template <template <class TT> class T> class A {
  typename T<int>::S s;
};

template <class U> class B {
private:
  struct S { /* ... */ };
};

A<B> b;             // ill-formed: A has no access to B​::​S

end example]

Когда используются пакеты аргументов шаблона или значения по умолчанию template-arguments , template-argument список может быть пустым. В этом случае пустые <> скобки по-прежнему должны использоваться как template-argument-list. [Example:

template<class T = char> class String;
String<>* p;                    // OK: String<char>
String* q;                      // syntax error
template<class ... Elements> class Tuple;
Tuple<>* t;                     // OK: Elements is empty
Tuple* u;                       // syntax error

end example]

Явный вызов деструктора ( [class.dtor]) для объекта, имеющего тип, являющийся специализацией шаблона класса, может явно указывать на template-arguments. [Example:

template<class T> struct A {
  ~A();
};
void f(A<int>* p, A<int>* q) {
  p->A<int>::~A();              // OK: destructor call
  q->A<int>::~A<int>();         // OK: destructor call
}

end example]

Если использование a template-argument приводит к неверно сформированной конструкции при создании экземпляра специализации шаблона, программа плохо сформирована.

Когда шаблон в a template-id является перегруженным шаблоном функции, игнорируются как нешаблонные функции в наборе перегрузки, так и шаблоны функций в наборе перегрузки, для которых template-arguments не совпадают template-parameters. Если ни один из шаблонов функций не соответствует template-parameters, программа имеет неправильный формат.

Когда simple-template-idне называют функцию, по умолчанию template-argumentэто , implicitly instantiated когда значение этого аргумента по умолчанию требуется. [Example:

template<typename T, typename U = int> struct S { };
S<bool>* p;         // the type of p is S<bool, int>*

Аргумент по умолчанию для U создается для формирования типа S<bool, int>*. ]end example

А, template-argumentза которым следует многоточие, - это pack expansion.

По умолчанию такой двусмысленности нет, template-argument потому что форма template-parameter определяет допустимые формы template-argument.

17.3.1 Template type arguments [temp.arg.type]

A template-argument для типа, template-parameter который является типом, должен быть a type-id.

[Example:

template <class T> class X { };
template <class T> void f(T t) { }
struct { } unnamed_obj;

void f() {
  struct A { };
  enum { e1 };
  typedef struct { } B;
  B b;
  X<A> x1;          // OK
  X<A*> x2;         // OK
  X<B> x3;          // OK
  f(e1);            // OK
  f(unnamed_obj);   // OK
  f(b);             // OK
}

end example] [ Note: Аргумент типа шаблона может быть неполным типом ( [basic.types]). ]end note

17.3.2 Template non-type arguments [temp.arg.nontype]

Если тип a template-parameter содержит a placeholder type, выводимый тип параметра определяется по типу template-argument by placeholder type deduction. Если выведенный тип параметра не разрешен для template-parameterобъявления ( [temp.param]), программа имеет неправильный формат.

A template-argument для нетипа template-parameter должен быть converted constant expression типа template-parameter. Для не являющегося типом template-parameterссылки или типа указателя значение константного выражения не должно ссылаться (или для типа указателя не должно быть адресом):

[ Note: Если template-argument представляет собой набор перегруженных функций (или указатель или указатель на член), функция сопоставления выбирается из set ( [over.over]). ]end note

[Example:

template<const int* pci> struct X { /* ... */ };
int ai[10];
X<ai> xi;                       // array to pointer and qualification conversions

struct Y { /* ... */ };
template<const Y& b> struct Z { /* ... */ };
Y y;
Z<y> z;                         // no conversion, but note extra cv-qualification

template<int (&pa)[5]> struct W { /* ... */ };
int b[5];
W<b> w;                         // no conversion

void f(char);
void f(int);

template<void (*pf)(int)> struct A { /* ... */ };

A<&f> a;                        // selects f(int)

template<auto n> struct B { /* ... */ };
B<5> b1;                        // OK: template parameter type is int
B<'a'> b2;                      // OK: template parameter type is char
B<2.5> b3;                      // error: template parameter type cannot be double

end example]

[ Note: A string literal не является приемлемым template-argument. [Example:

template<class T, const char* p> class X {
  /* ... */
};

X<int, "Studebaker"> x1;        // error: string literal as template-argument

const char p[] = "Vivisectionist";
X<int,p> x2;                    // OK

end example] ]end note

[ Note: Адрес элемента массива или нестатического элемента данных недопустим template-argument. [Example:

template<int* p> class X { };

int a[10];
struct S { int m; static int s; } s;

X<&a[2]> x3;                    // error: address of array element
X<&s.m> x4;                     // error: address of non-static member
X<&s.s> x5;                     // OK: address of static member
X<&S::s> x6;                    // OK: address of static member

end example] ]end note

[ Note: Временный объект неприемлем, template-argument если соответствующий template-parameter имеет ссылочный тип. [Example:

template<const int& CRI> struct B { /* ... */ };

B<1> b2;                        // error: temporary would be required for template argument

int c = 1;
B<c> b1;                        // OK

end example] ]end note

17.3.3 Template template arguments [temp.arg.template]

A template-argument для шаблона template-parameter должно быть именем шаблона класса или шаблона псевдонима, выраженным как id-expression. При template-argumentименовании шаблона класса учитываются только первичные шаблоны классов при сопоставлении аргумента шаблона шаблона с соответствующим параметром; частичные специализации не рассматриваются, даже если их списки параметров совпадают со списком параметров шаблона.

Любые partial specializations связанные с шаблоном первичного класса или шаблоном первичной переменной рассматриваются при создании template-parameter экземпляра специализации, основанной на шаблоне . Если специализация не видна в момент создания экземпляра и была бы выбрана, если бы была видима, программа имеет неправильный формат, диагностическая проверка не требуется. [Example:

template<class T> class A {     // primary template
  int x;
};
template<class T> class A<T*> { // partial specialization
  long x;
};
template<template<class U> class V> class C {
  V<int>  y;
  V<int*> z;
};
C<A> c;             // V<int> within C<A> uses the primary template, so c.y.x has type int
                    // V<int*> within C<A> uses the partial specialization, so c.z.x has type long

end example]

Соответствует template-argumentшаблону, template-parameter P если P он не менее специализирован, чем template-argument A. Если P содержит пакет параметров, то A также соответствует, P если каждый из Aпараметров шаблона совпадает с соответствующим параметром шаблона в template-parameter-listиз P. Два параметра шаблона совпадают, если они одного типа (тип, не тип, шаблон), для не-типа template-parametersих типы эквивалентны ( [temp.over.link]), а для шаблона template-parameters, каждое из их соответствующих template-parameters совпадений, рекурсивно. Если P's template-parameter-listсодержит a template parameter pack, пакет параметров шаблона будет соответствовать нулю или более параметрам шаблона или пакетам параметров шаблона в template-parameter-listиз A с тем же типом и формой, что и пакет параметров шаблона в P (игнорируя, являются ли эти параметры шаблона пакетами параметров шаблона).

[Example:

template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class ... Types> class C { /* ... */ };
template<auto n> class D { /* ... */ };
template<template<class> class P> class X { /* ... */ };
template<template<class ...> class Q> class Y { /* ... */ };
template<template<int> class R> class Z { /* ... */ };

X<A> xa;            // OK
X<B> xb;            // OK
X<C> xc;            // OK
Y<A> ya;            // OK
Y<B> yb;            // OK
Y<C> yc;            // OK
Z<D> zd;            // OK

end example]

[Example:

template <class T> struct eval;

template <template <class, class...> class TT, class T1, class... Rest>
struct eval<TT<T1, Rest...>> { };

template <class T1> struct A;
template <class T1, class T2> struct B;
template <int N> struct C;
template <class T1, int N> struct D;
template <class T1, class T2, int N = 17> struct E;

eval<A<int>> eA;            // OK: matches partial specialization of eval
eval<B<int, float>> eB;     // OK: matches partial specialization of eval
eval<C<17>> eC;             // error: C does not match TT in partial specialization
eval<D<int, 17>> eD;        // error: D does not match TT in partial specialization
eval<E<int, float>> eE;     // error: E does not match TT in partial specialization

end example]

Шаблон template-parameter P является, по крайней мере, таким же специализированным, как шаблон, template-argument A если, учитывая следующую перезапись двух шаблонов функций, шаблон функции, соответствующий, по P крайней мере, так же специализирован, как шаблон функции, соответствующий в A соответствии с partial ordering rules for function templates. Учитывая изобретенный шаблон класса X со списком параметров шаблона A (включая аргументы по умолчанию):

  • Каждый из двух шаблонов функций имеет те же параметры шаблона, что и P или A.

  • Каждый шаблон функции имеет один параметр функции, тип которого является специализацией X с аргументами шаблона, соответствующими параметрам шаблона из соответствующего шаблона функции, где для каждого параметра PP шаблона в списке параметров шаблона шаблона функции формируется соответствующий аргумент шаблона AA . Если PP объявляется пакет параметров, то AA это расширение пакета PP... ( [temp.variadic]); в противном случае AA - это id-expression PP.

Если при перезаписи получается недопустимый тип, то P он не такой специализированный, как A.

17.4 Type equivalence [temp.type]

Два template-ids относятся к одному и тому же классу, функции или переменной, если

  • их template-names, operator-function-idsили literal-operator-ids ссылаются на тот же шаблон и

  • их соответствующий тип template-arguments - того же типа и

  • соответствующие им не типовые аргументы шаблона целочисленного или перечислимого типа имеют идентичные значения и

  • соответствующий им не тип template-arguments указателя типа относится к одному и тому же объекту или функции или является как значением нулевого указателя, так и

  • их соответствующий не тип template-arguments типа указатель на член относится к одному и тому же члену класса или является как значением указателя нулевого члена, так и

  • соответствующие template-arguments им ссылочные типы, не являющиеся типами, относятся к одному и тому же объекту или функции и

  • их соответствующий шаблон template-arguments относится к одному и тому же шаблону.

[Example:

template<class E, int size> class buffer { /* ... */ };
buffer<char,2*512> x;
buffer<char,1024> y;

объявляет x и y быть одного типа, и

template<class T, void(*err_fct)()> class list { /* ... */ };
list<int,&error_handler1> x1;
list<int,&error_handler2> x2;
list<int,&error_handler2> x3;
list<char,&error_handler2> x4;

объявляет x2 и x3 быть одного типа. Их тип отличается от типов x1 и x4.

template<class T> struct X { };
template<class> struct Y { };
template<class T> using Z = Y<T>;
X<Y<int> > y;
X<Z<int> > z;

объявляет y и z быть одного типа. ]end example

Если выражение e равно type-dependent, decltype(e) обозначает уникальный зависимый тип. Два таких decltype-specifiers относятся к одному и тому же типу, только если expressions они эквивалентны ( [temp.over.link]). [ Note: Однако такой тип может иметь псевдоним, например, a typedef-name. ] end note

17.5 Template declarations [temp.decls]

A template-id, то есть template-name символ , за которым следует a template-argument-list , не должен указываться в объявлении первичного объявления шаблона. [Example:

template<class T1, class T2, int I> class A<T1, T2, I> { };     // error
template<class T1, int I> void sort<T1, I>(T1 data[I]);         // error

end example] [ Note: Однако этот синтаксис разрешен в class template partial specializations. ]end note

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

Поскольку объект alias-declarationне может объявить a template-id, невозможно частично или явно специализировать шаблон псевдонима.

17.5.1 Class templates [temp.class]

A class template определяет макет и операции для неограниченного набора связанных типов.

[ Example: Один шаблон класса List может предоставлять неограниченный набор определений классов: по одному классу List<T> для каждого типа T, каждое из которых описывает связанный список элементов типа T. Точно так же шаблон класса, Array описывающий непрерывный динамический массив, может быть определен следующим образом:

template<class T> class Array {
  T* v;
  int sz;
public:
  explicit Array(int);
  T& operator[](int);
  T& elem(int i) { return v[i]; }
};

Префикс template<class T> указывает, что шаблон объявляется и что в объявлении type-name T можно использовать a . Другими словами, Array это параметризованный тип с T параметром. ]end example

Когда функция-член, класс-член, перечисление членов, статический член-данные или шаблон-член шаблона класса определены вне определения шаблона класса, определение члена определяется как определение шаблона, в котором template-parameters есть те из шаблон класса. Имена параметров шаблона, используемые в определении члена, могут отличаться от имен параметров шаблона, используемых в определении шаблона класса. Список аргументов шаблона, следующий за именем шаблона класса в определении элемента, должен называть параметры в том же порядке, что и в списке параметров шаблона элемента. Каждый пакет параметров шаблона должен быть расширен многоточием в списке аргументов шаблона. [Example:

template<class T1, class T2> struct A {
  void f1();
  void f2();
};

template<class T2, class T1> void A<T2,T1>::f1() { }    // OK
template<class T2, class T1> void A<T1,T2>::f2() { }    // error
template<class ... Types> struct B {
  void f3();
  void f4();
};

template<class ... Types> void B<Types ...>::f3() { }    // OK
template<class ... Types> void B<Types>::f4() { }        // error

end example]

При повторном объявлении, частичной специализации, явной специализации или явном создании экземпляра шаблона класса он class-key должен согласовываться по сути с объявлением исходного шаблона класса ( [dcl.type.elab]).

17.5.1.1 Member functions of class templates [temp.mem.func]

Функция-член шаблона класса может быть определена вне определения шаблона класса, в котором она объявлена. [Example:

template<class T> class Array {
  T* v;
  int sz;
public:
  explicit Array(int);
  T& operator[](int);
  T& elem(int i) { return v[i]; }
};

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

template<class T> T& Array<T>::operator[](int i) {
  if (i<0 || sz<=i) error("Array: range error");
  return v[i];
}

end example]

Для template-arguments функции-члена шаблона класса определяется template-arguments тип объекта, для которого вызывается функция-член. [ Для будет определяться , к которому применяется операция индексации.Example: template-argumentArray<T>​::​operator[]()Array

Array<int> v1(20);
Array<dcomplex> v2(30);

v1[3] = 7;                      // Array<int>​::​operator[]()
v2[3] = dcomplex(7,8);          // Array<dcomplex>​::​operator[]()

end example]

17.5.1.2 Member classes of class templates [temp.mem.class]

Класс-член шаблона класса может быть определен вне определения шаблона класса, в котором он объявлен. [ Note: Класс-член должен быть определен до его первого использования, которое требует создания экземпляра ( [temp.inst]). Например,

template<class T> struct A {
  class B;
};
A<int>::B* b1;                  // OK: requires A to be defined but not A​::​B
template<class T> class A<T>::B { };
A<int>::B  b2;                  // OK: requires A​::​B to be defined

end note]

17.5.1.3 Static data members of class templates [temp.static]

Определение для статического элемента данных или шаблона статического элемента данных может быть предоставлено в области пространства имен, включающей определение шаблона класса статического члена. [Example:

template<class T> class X {
  static T s;
};
template<class T> T X<T>::s = 0;

struct limits {
  template<class T>
    static const T min;         // declaration
};

template<class T>
  const T limits::min = { };    // definition

end example]

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

template <class T> struct A {
  static int i[];
};
template <class T> int A<T>::i[4];              // 4 elements
template <> int A<int>::i[] = { 1 };            // OK: 1 element

end example]

17.5.1.4 Enumeration members of class templates [temp.mem.enum]

Член перечисления шаблона класса может быть определен вне определения шаблона класса. [Example:

template<class T> struct A {
  enum E : T;
};
A<int> a;
template<class T> enum A<T>::E : T { e1, e2 };
A<int>::E e = A<int>::e1;

end example]

17.5.2 Member templates [temp.mem]

Шаблон может быть объявлен в классе или шаблоне класса; такой шаблон называется шаблоном члена. Шаблон элемента может быть определен внутри или вне его определения класса или определения шаблона класса. Шаблон элемента шаблона класса, который определен вне его определения шаблона класса, должен быть указан с template-parameters помощью шаблона класса, за которым следует template-parameters элемент шаблона элемента. [Example:

template<class T> struct string {
  template<class T2> int compare(const T2&);
  template<class T2> string(const string<T2>& s) { /* ... */ }
};

template<class T> template<class T2> int string<T>::compare(const T2& s) {
}

end example]

Локальный класс незамкнутого типа не должен иметь шаблонов членов. Access control rules применяются к именам шаблонов элементов. Деструктор не должен быть шаблоном члена. В [dcl.fct]классе могут быть объявлены нешаблонная функция-член ( ) с заданным именем и типом, а также шаблон функции-члена с тем же именем, который можно использовать для создания специализации одного и того же типа. Если оба существуют, использование этого имени и типа относится к члену, не являющемуся шаблоном, если не указан явный список аргументов шаблона. [Example:

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

template <> void A<int>::f(int) { }                 // non-template member function
template <> template <> void A<int>::f<>(int) { }   // member function template specialization

int main() {
  A<char> ac;
  ac.f(1);          // non-template
  ac.f('c');        // template
  ac.f<>(1);        // template
}

end example]

Шаблон функции-члена не должен быть виртуальным. [Example:

template <class T> struct AA {
  template <class C> virtual void g(C);     // error
  virtual void f();                         // OK
};

end example]

Специализация шаблона функции-члена не отменяет виртуальную функцию из базового класса. [Example:

class B {
  virtual void f(int);
};

class D : public B {
  template <class T> void f(T); // does not override B​::​f(int)
  void f(int i) { f<>(i); }     // overriding function that calls the template instantiation
};

end example]

На специализацию шаблона функции преобразования ссылаются так же, как на нешаблонную функцию преобразования, которая преобразуется в тот же тип. [Example:

struct A {
  template <class T> operator T*();
};
template <class T> A::operator T*(){ return 0; }
template <> A::operator char*(){ return 0; }    // specialization
template A::operator void*();                   // explicit instantiation

int main() {
  A a;
  int* ip;
  ip = a.operator int*();       // explicit call to template operator A​::​operator int*()
}

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

Специализация шаблона функции преобразования не может быть найдена с помощью поиска по имени. Вместо этого рассматриваются любые шаблоны функций преобразования, видимые в контексте использования. Для каждого такого оператора, если вывод аргумента завершается успешно ( [temp.deduct.conv]), полученная специализация используется, как если бы она была найдена с помощью поиска по имени.

A using-declarationв производном классе не может ссылаться на специализацию шаблона функции преобразования в базовом классе.

Overload resolution и partial ordering используются для выбора наилучшей функции преобразования среди множества специализаций шаблонов функций преобразования и / или функций преобразования, не являющихся шаблонами.

17.5.3 Variadic templates [temp.variadic]

A template parameter pack - это параметр шаблона, который принимает ноль или более аргументов шаблона. [Example:

template<class ... Types> struct Tuple { };

Tuple<> t0;                     // Types contains no arguments
Tuple<int> t1;                  // Types contains one argument: int
Tuple<int, float> t2;           // Types contains two arguments: int and float
Tuple<0> error;                 // error: 0 is not a type

end example]

A function parameter pack - это параметр функции, который принимает ноль или более аргументов функции. [Example:

template<class ... Types> void f(Types ... args);

f();                            // OK: args contains no arguments
f(1);                           // OK: args contains one argument: int
f(2, 1.0);                      // OK: args contains two arguments: int and double

end example]

A parameter pack - это либо пакет параметров шаблона, либо пакет параметров функции.

A pack expansion состоит из a pattern и многоточия, создание экземпляра которого дает ноль или более экземпляров шаблона в списке (описанном ниже). Форма паттерна зависит от контекста, в котором происходит расширение. Расширения пакетов могут происходить в следующих контекстах:

[Example:

template<class ... Types> void f(Types ... rest);
template<class ... Types> void g(Types ... rest) {
  f(&rest ...);     // “&rest ...” is a pack expansion; “&rest” is its pattern
}

end example]

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

Пакет параметров, имя которого появляется в шаблоне расширения пакета, расширяется этим расширением пакета. Появление имени пакета параметров расширяется только расширением самого внутреннего охватывающего пакета. Шаблон расширения пакета должен называть один или несколько пакетов параметров, которые не расширяются вложенным расширением пакета; такие пакеты параметров вызываются unexpanded parameter packs в шаблоне. Все пакеты параметров, расширенные расширением пакета, должны иметь одинаковое количество аргументов. Имя пакета параметров, который не раскрывается, отображается неправильно. [Example:

template<typename...> struct Tuple {};
template<typename T1, typename T2> struct Pair {};

template<class ... Args1> struct zip {
  template<class ... Args2> struct with {
    typedef Tuple<Pair<Args1, Args2> ... > type;
  };
};

typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
    // T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned>>
typedef zip<short>::with<unsigned short, unsigned>::type T2;
    // error: different number of arguments specified for Args1 and Args2

template<class ... Args>
  void g(Args ... args) {                   // OK: Args is expanded by the function parameter pack args
    f(const_cast<const Args*>(&args)...);   // OK: “Args” and “args” are expanded
    f(5 ...);                               // error: pattern does not contain any parameter packs
    f(args);                                // error: parameter pack “args” is not expanded
    f(h(args ...) + args ...);              // OK: first “args” expanded within h,
                                            // second “args” expanded within f
  }

end example]

Создание экземпляра расширения пакета, которое не является ни sizeof... выражением, ни a, fold-expression создает список E1,E2,,EN, где N - количество элементов в параметрах расширения пакета. Каждый Ei генерируется путем создания экземпляра шаблона и замены каждого параметра расширения пакета его ith элементом. Такой элемент в контексте создания интерпретируется следующим образом:

  • если пакет является пакетом параметров шаблона, это элемент template parameter соответствующего типа (тип или нет), обозначающий тип или значение из аргумента шаблона; иначе,

  • если пакет является пакетом параметров функции, элемент является id-expression обозначением параметра функции, который возник в результате создания экземпляра шаблона, в котором объявлен пакет.

Все Ei элементы стали в прилагающемся списке. [ Note: Разнообразие списка изменяется в зависимости от контекста: expression-list, base-specifier-list, template-argument-listи т.д. ] Если равен нуль, то конкретизация расширения производит пустой список. Такое создание экземпляра не изменяет синтаксическую интерпретацию включающей конструкции, даже в тех случаях, когда полное исключение списка в противном случае было бы некорректным или привело бы к двусмысленности в грамматике. [end note N Example:

template<class... T> struct X : T... { };
template<class... T> void f(T... values) {
  X<T...> x(values...);
}

template void f<>();    // OK: X<> has no base classes
                        // x is a variable of type X<> that is value-initialized

end example]

Создание экземпляра a sizeof... expression создает целочисленную константу, содержащую количество элементов в расширяемом пакете параметров.

Создание экземпляра a fold-expressionпроизводит:

  • ((E1 op E2) op ) op EN для унарной левой складки,

  • E1 op ( op (EN1 op EN)) для унарной правой складки,

  • (((E op E1) op E2) op ) op EN для двоичной левой складки, и

  • E1 op ( op (EN1 op (EN op E))) для двоичной правой складки.

В каждом случае op это fold-operator, N является количеством элементов в параметрах расширения пакета, и каждый Ei генерируется инстанцирование шаблона и замены каждого параметра разложения пакета с его iго элементом. Для двоичного свернутого выражения E создается путем создания экземпляра cast-expression , не содержащего нерасширенного пакета параметров. [Example:

template<typename ...Args>
  bool all(Args ...args) { return (... && args); }

bool b = all(true, true, true, false);

Внутри экземпляра allвозвращаемое выражение расширяется до ((true && true) && true) && false, что дает результат false. ] Если для унарного свернутого выражения равно нулю, значение выражения показано в таблице ; если оператор не указан в таблице , создание экземпляра некорректно.end example N 14 14

Таблица 14 - Значение сворачивания пустых последовательностей
ОператорЗначение, когда пакет параметров пуст
&& true
|| false
, void()

17.5.4 Friends [temp.friend]

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

  • если имя друга является квалифицированным или неквалифицированным template-id, объявление друга относится к специализации шаблона функции, в противном случае,

  • если имя друга - a qualified-idи соответствующая функция, не являющаяся шаблоном, найдена в указанном классе или пространстве имен, объявление друга ссылается на эту функцию, в противном случае,

  • если имя друга - a qualified-idи соответствующий шаблон функции найден в указанном классе или пространстве имен, объявление друга ссылается на выведенную специализацию этого шаблона функции ( [temp.deduct.decl]), в противном случае,

  • имя должно быть объявлено unqualified-id(или повторно объявлено) нешаблонной функцией.

[Example:

template<class T> class task;
template<class T> task<T>* preempt(task<T>*);

template<class T> class task {
  friend void next_time();
  friend void process(task<T>*);
  friend task<T>* preempt<T>(task<T>*);
  template<class C> friend int func(C);

  friend class task<int>;
  template<class P> friend class frd;
};

Здесь каждая специализация task шаблона класса выполняет функцию next_­time друга; поскольку process не имеет явного значения template-arguments, каждая специализация task шаблона класса имеет process в качестве друга соответствующую типизированную функцию , и этот друг не является специализацией шаблона функции; поскольку у друга preempt есть явная template-argument T, каждая специализация task шаблона класса имеет соответствующую специализацию шаблона функции preempt как друга; и каждая специализация task шаблона класса имеет все специализации шаблона функции func как друзей. Точно так же каждая специализация task шаблона класса имеет специализацию шаблона класса task<int> как друга и все специализации шаблона класса frd как друзья. ]end example

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

class A {
  template<class T> friend class B;                 // OK
  template<class T> friend void f(T){ /* ... */ }   // OK
};

end example]

А друг шаблон декларации указывает , что все специализации этого шаблона, являются ли они implicitly instantiated, partially specialized или explicitly specialized, являются друзьями класса , содержащего декларацию друга шаблон. [Example:

class X {
  template<class T> friend struct A;
  class Y { };
};

template<class T> struct A { X::Y ab; };            // OK
template<class T> struct A<T*> { X::Y ab; };        // OK

end example]

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

template<class T> struct A {
  struct B { };
  void f();
  struct D {
    void g();
  };
};
template<> struct A<int> {
  struct B { };
  int f();
  struct D {
    void g();
  };
};

class C {
  template<class T> friend struct A<T>::B;      // grants friendship to A<int>​::​B even though
                                                // it is not a specialization of A<T>​::​B
  template<class T> friend void A<T>::f();      // does not grant friendship to A<int>​::​f()
                                                // because its return type does not match
  template<class T> friend void A<T>::D::g();   // does not grant friendship to A<int>​::​D​::​g()
                                                // because A<int>​::​D is not a specialization of A<T>​::​D
};

end example]

[ Note: Объявление друга может сначала объявлять член охватывающей области пространства имен ( [temp.inject]). ]end note

Шаблон друга не должен быть объявлен в локальном классе.

В дружеских объявлениях не указывается частичная специализация. [Example:

template<class T> class A { };
class X {
  template<class T> friend class A<T*>;         // error
};

end example]

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

17.5.5 Class template partial specializations [temp.class.spec]

primary class template Декларация, в которой имя шаблона класса является идентификатором. Объявление шаблона , в котором имя шаблона класса является simple-template-id это partial specialization шаблон класса с именем в simple-template-id. Частичная специализация шаблона класса обеспечивает альтернативное определение шаблона, которое используется вместо основного определения, когда аргументы в специализации совпадают с аргументами, указанными в частичной специализации ( [temp.class.spec.match]). Первичный шаблон должен быть объявлен перед любыми специализациями этого шаблона. Частичная специализация должна быть объявлена ​​перед первым использованием специализации шаблона класса, которая будет использовать частичную специализацию как результат неявной или явной реализации в каждой единице перевода, в которой такое использование происходит; Диагностика не требуется.

Каждая частичная специализация шаблона класса является отдельным шаблоном, и должны быть предоставлены определения для членов частичной специализации шаблона ( [temp.class.spec.mfunc]).

[Example:

template<class T1, class T2, int I> class A             { };
template<class T, int I>            class A<T, T*, I>   { };
template<class T1, class T2, int I> class A<T1*, T2, I> { };
template<class T>                   class A<int, T*, 5> { };
template<class T1, class T2, int I> class A<T1, T2*, I> { };

Первое объявление объявляет первичный (неспециализированный) шаблон класса. Второе и последующие объявления объявляют частичную специализацию первичного шаблона. ]end example

Параметры шаблона указываются в прилагаемом списке угловых скобок, который следует сразу за ключевым словом template. Для частичных специализаций список аргументов шаблона явно записывается сразу после имени шаблона класса. Для первичных шаблонов этот список неявно описывается списком параметров шаблона. В частности, порядок аргументов шаблона - это последовательность, в которой они появляются в списке параметров шаблона. [ Example: Список аргументов шаблона для основного шаблона в примере выше <T1, T2, I>. ] [ Список аргументов шаблона не должен указываться в объявлении первичного шаблона. Например,end exampleNote:

template<class T1, class T2, int I>
class A<T1, T2, I> { };                         // error

end note]

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

template<class T> struct A {
  struct C {
    template<class T2> struct B { };
    template<class T2> struct B<T2**> { };      // partial specialization #1
  };
};

// partial specialization of A<T>​::​C​::​B<T2>
template<class T> template<class T2>
  struct A<T>::C::B<T2*> { };                   // #2

A<short>::C::B<int*> absip;                     // uses partial specialization #2

end example]

Сами по себе объявления частичной специализации не могут быть найдены поиском по имени. Скорее, когда используется имя первичного шаблона, также учитываются любые ранее объявленные частичные специализации первичного шаблона. Одним из следствий этого является то, что a, using-declaration который ссылается на шаблон класса, не ограничивает набор частичных специализаций, которые можно найти через using-declaration. [Example:

namespace N {
  template<class T1, class T2> class A { };     // primary template
}

using N::A;                                     // refers to the primary template

namespace N {
  template<class T> class A<T, T*> { };         // partial specialization
}

A<int,int*> a;      // uses the partial specialization, which is found through the using-declaration
                    // which refers to the primary template

end example]

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

В списке аргументов частичной специализации шаблона класса действуют следующие ограничения:

  • Тип параметра шаблона, соответствующий специализированному аргументу, не являющемуся типом, не должен зависеть от параметра специализации. [Example:

    template <class T, T t> struct C {};
    template <class T> struct C<T, 1>;              // error
    
    template< int X, int (*array_ptr)[X] > class A {};
    int array[5];
    template< int X > class A<X,&array> { };        // error
    

    end example]

  • Специализация должна отличаться more specialized от основного шаблона.

  • Список параметров шаблона для специализации не должен содержать значений аргументов шаблона по умолчанию.138

  • Аргумент не должен содержать нерасширенный пакет параметров. Если аргумент - это pack expansion, он должен быть последним аргументом в списке аргументов шаблона.

Их невозможно использовать.

17.5.5.1 Matching of class template partial specializations [temp.class.spec.match]

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

  • Если найдена ровно одна подходящая специализация, экземпляр создается на основе этой специализации.

  • Если найдено более одной подходящей специализации, partial order rules используются для определения, является ли одна из специализаций более специализированной, чем другие. Если ни одна из специализаций не является более специализированной, чем все другие соответствующие специализации, тогда использование шаблона класса неоднозначно и программа плохо сформирована.

  • Если совпадений не найдено, экземпляр создается из основного шаблона.

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

template<class T1, class T2, int I> class A             { };    // #1
template<class T, int I>            class A<T, T*, I>   { };    // #2
template<class T1, class T2, int I> class A<T1*, T2, I> { };    // #3
template<class T>                   class A<int, T*, 5> { };    // #4
template<class T1, class T2, int I> class A<T1, T2*, I> { };    // #5

A<int, int, 1>   a1;            // uses #1
A<int, int*, 1>  a2;            // uses #2, T is int, I is 1
A<int, char*, 5> a3;            // uses #4, T is char
A<int, char*, 1> a4;            // uses #5, T1 is int, T2 is char, I is 1
A<int*, int*, 2> a5;            // ambiguous: matches #3 and #5

end example]

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

template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {};     // error

template <int I> struct A<I, I> {};         // OK

template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {};    // OK

end example]

В имени типа, которое относится к специализации шаблона класса, (например, A<int, int, 1>) список аргументов должен соответствовать списку параметров шаблона первичного шаблона. Аргументы шаблона специализации выводятся из аргументов первичного шаблона.

17.5.5.2 Partial ordering of class template specializations [temp.class.order]

Для двух частичных специализаций шаблона класса первая является more specialized более высокой, чем вторая, если при следующей перезаписи двух шаблонов функций первый шаблон функции более специализирован, чем второй, в соответствии с ordering rules for function templates:

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

  • Каждый шаблон функции имеет один параметр функции, тип которого является шаблон класса специализацией , где аргументы шаблона являются соответствующими параметрами шаблона из шаблона функции для каждого аргумента шаблона в template-argument-list из simple-template-id частичной специализации.

[Example:

template<int I, int J, class T> class X { };
template<int I, int J>          class X<I, J, int> { };         // #1
template<int I>                 class X<I, I, int> { };         // #2

template<int I0, int J0> void f(X<I0, J0, int>);                // A
template<int I0>         void f(X<I0, I0, int>);                // B

template <auto v>    class Y { };
template <auto* p>   class Y<p> { };                            // #3
template <auto** pp> class Y<pp> { };                           // #4

template <auto* p0>   void g(Y<p0>);                            // C
template <auto** pp0> void g(Y<pp0>);                           // D

Согласно правилам упорядочивания шаблонов функций, шаблон функции B более специализирован, чем шаблон A функции, а шаблон функции D более специализирован, чем шаблон функции C. Следовательно, частичная специализация №2 более специализирована, чем частичная специализация №1, а частичная специализация №4 более специализирована, чем частичная специализация №3. ]end example

17.5.5.3 Members of class template specializations [temp.class.spec.mfunc]

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

// primary class template
template<class T, int I> struct A {
  void f();
};

// member of primary class template
template<class T, int I> void A<T,I>::f() { }

// class template partial specialization
template<class T> struct A<T,2> {
  void f();
  void g();
  void h();
};

// member of class template partial specialization
template<class T> void A<T,2>::g() { }

// explicit specialization
template<> void A<char,2>::h() { }

int main() {
  A<char,0> a0;
  A<char,2> a2;
  a0.f();           // OK, uses definition of primary template's member
  a2.g();           // OK, uses definition of partial specialization's member
  a2.h();           // OK, uses definition of explicit specialization's member
  a2.f();           // ill-formed, no definition of f for A<T,2>; the primary template is not used here
}

end example]

Если шаблон элемента шаблона класса является частично специализированным, частичные специализации шаблона элемента являются шаблонами элементов включающего шаблона класса; если создается экземпляр шаблона включающего класса ( [temp.inst], [temp.explicit]), объявление для каждой частичной специализации шаблона элемента также создается как часть создания членов специализации шаблона класса. Если первичный шаблон члена явно специализирован для данной (неявной) специализации шаблона включающего класса, частичные специализации шаблона члена игнорируются для этой специализации шаблона включающего класса. Если частичная специализация шаблона члена явно специализирована для данной (неявной) специализации шаблона включающего класса, основной шаблон члена и другие его частичные специализации по-прежнему рассматриваются для этой специализации шаблона включающего класса. [Example:

template<class T> struct A {
  template<class T2> struct B {};                     // #1
  template<class T2> struct B<T2*> {};                // #2
};

template<> template<class T2> struct A<short>::B {};  // #3

A<char>::B<int*>  abcip;  // uses #2
A<short>::B<int*> absip;  // uses #3
A<char>::B<int>  abci;    // uses #1

end example]

17.5.6 Function templates [temp.fct]

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

template<class T> class Array { };
template<class T> void sort(Array<T>&);

end example]

Шаблон функции может быть перегружен другими шаблонами функций и нешаблонными функциями ( [dcl.fct]). Функция, не являющаяся шаблоном, не связана с шаблоном функции (т. Е. Никогда не считается специализацией), даже если она имеет то же имя и тип, что и потенциально сгенерированная специализация шаблона функции.139

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

17.5.6.2 Partial ordering of function templates [temp.func.order]

Если шаблон функции перегружен, использование специализации шаблона функции может быть неоднозначным, поскольку template argument deduction может связать специализацию шаблона функции с более чем одним объявлением шаблона функции. Partial ordering объявлений перегруженных шаблонов функций используется в следующих контекстах для выбора шаблона функции, на который ссылается специализация шаблона функции:

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

Чтобы создать преобразованный шаблон, для каждого типа, не-типа или параметра шаблона шаблона (включая template parameter packs их) синтезируйте уникальный тип, значение или шаблон класса соответственно и заменяйте его для каждого вхождения этого параметра в типе функции шаблона. [ Note: Тип, заменяющий заполнитель в типе значения, синтезированного для параметра шаблона, не являющегося типом, также является уникальным синтезированным типом. ] Если только один из шаблонов функций является нестатическим членом некоторого класса , считается, что в его список параметров функции вставлен новый первый параметр. Принимая во внимание , как CV-классификаторов (если таковые имеются), новый параметр имеет тип «ссылки на RValue » , если по желанию из вне или , если не имеет , и первый параметр другого шаблона имеет RValue ссылочный тип. В противном случае новый параметр имеет тип «ссылка lvalue на ». [ Это позволяет упорядочивать нестатический член по отношению к функции, не являющейся членом, а результаты должны быть эквивалентны упорядочению двух эквивалентных нечленов. ] [end note M A M cv Mcv Aref-qualifierM && M ref-qualifier cv ANote: end noteExample:

struct A { };
template<class T> struct B {
  template<class R> int operator*(R&);              // #1
};

template<class T, class R> int operator*(T&, R&);   // #2

// The declaration of B​::​operator* is transformed into the equivalent of
// template<class R> int operator*(B<A>&, R&);      // #1a

int main() {
  A a;
  B<A> b;
  b * a;                                            // calls #1a
}

end example]

Используя тип функции преобразованного шаблона функции, выполните определение типа для другого шаблона, как описано в [temp.deduct.partial].

[Example:

template<class T> struct A { A(); };

template<class T> void f(T);
template<class T> void f(T*);
template<class T> void f(const T*);

template<class T> void g(T);
template<class T> void g(T&);

template<class T> void h(const T&);
template<class T> void h(A<T>&);

void m() {
  const int* p;
  f(p);             // f(const T*) is more specialized than f(T) or f(T*)
  float x;
  g(x);             // ambiguous: g(T) or g(T&)
  A<int> z;
  h(z);             // overload resolution selects h(A<T>&)
  const A<int> z2;
  h(z2);            // h(const T&) is called because h(A<T>&) is not callable
}

end example]

[ Note: Поскольку частичное упорядочение в контексте вызова учитывает только параметры, для которых есть явные аргументы вызова, некоторые параметры игнорируются (а именно, пакеты параметров функций, параметры с аргументами по умолчанию и параметры с многоточием). [Example:

template<class T> void f(T);                            // #1
template<class T> void f(T*, int=1);                    // #2
template<class T> void g(T);                            // #3
template<class T> void g(T*, ...);                      // #4
int main() {
  int* ip;
  f(ip);                                                // calls #2
  g(ip);                                                // calls #4
}

end example] [Example:

template<class T, class U> struct A { };

template<class T, class U> void f(U, A<U, T>* p = 0);   // #1
template<         class U> void f(U, A<U, U>* p = 0);   // #2
template<class T         > void g(T, T = T());          // #3
template<class T, class... U> void g(T, U ...);         // #4

void h() {
  f<int>(42, (A<int, int>*)0);                          // calls #2
  f<int>(42);                                           // error: ambiguous
  g(42);                                                // error: ambiguous
}

end example] [Example:

template<class T, class... U> void f(T, U...);          // #1
template<class T            > void f(T);                // #2
template<class T, class... U> void g(T*, U...);         // #3
template<class T            > void g(T);                // #4

void h(int i) {
  f(&i);                                                // error: ambiguous
  g(&i);                                                // OK: calls #3
}

end example] ] end note

17.5.7 Alias templates [temp.alias]

template-declaration, В котором declarationэто alias-declarationобъявляет identifierбыть alias template. Шаблон псевдонима - это имя семейства типов. Имя шаблона псевдонима - template-name.

Когда template-idотносится к специализации шаблона псевдонима, это равносильно тому , ассоциированному типа , полученный путем замены сва template-arguments для template-parameters в type-idшаблоне псевдонима. [ Note: Имя шаблона псевдонима никогда не выводится. ] [end noteExample:

template<class T> struct Alloc { /* ... */ };
template<class T> using Vec = vector<T, Alloc<T>>;
Vec<int> v;         // same as vector<int, Alloc<int>> v;

template<class T>
  void process(Vec<T>& v)
  { /* ... */ }

template<class T>
  void process(vector<T, Alloc<T>>& w)
  { /* ... */ }     // error: redefinition

template<template<class> class TT>
  void f(TT<int>);

f(v);               // error: Vec not deduced

template<template<class,class> class TT>
  void g(TT<int, Alloc<int>>);
g(v);               // OK: TT = vector

end example]

Однако, если template-idэто зависит, последующая подстановка аргументов шаблона по-прежнему применяется к template-id. [Example:

template<typename...> using void_t = void;
template<typename T> void_t<typename T::foo> f();
f<int>();           // error, int does not have a nested type foo

end example]

В type-idобъявлении шаблона псевдонима не должно быть ссылки на объявляемый шаблон псевдонима. Тип, созданный специализацией шаблона псевдонима, не должен прямо или косвенно использовать эту специализацию. [Example:

template <class T> struct A;
template <class T> using B = typename A<T>::U;
template <class T> struct A {
  typedef B<T> U;
};
B<short> b;         // error: instantiation of B<short> uses own type via A<short>​::​U

end example]

17.6 Name resolution [temp.res]

В определении шаблона можно использовать три вида имен:

  • Имя самого шаблона и имена, объявленные в самом шаблоне.

  • Имена, зависящие от template-parameter( [temp.dep]).

  • Имена из областей видимости в определении шаблона.

Предполагается, что имя, используемое в объявлении или определении шаблона и зависящее от a, template-parameter не именует тип, если только применимый поиск имени не найдет имя типа или имя не уточнено ключевым словом typename. [Example:

// no B declared here

class X;

template<class T> class Y {
  class Z;                      // forward declaration of member class

  void f() {
    X* a1;                      // declare pointer to X
    T* a2;                      // declare pointer to T
    Y* a3;                      // declare pointer to Y<T>
    Z* a4;                      // declare pointer to Z
    typedef typename T::A TA;
    TA* a5;                     // declare pointer to T's A
    typename T::A* a6;          // declare pointer to T's A
    T::A* a7;                   // T​::​A is not a type name:
                                // multiplication of T​::​A by a7; ill-formed, no visible declaration of a7
    B* a8;                      // B is not a type name:
                                // multiplication of B by a8; ill-formed, no visible declarations of B and a8
  }
};

end example]

Если a qualified-idпредназначен для ссылки на тип, который не является членом текущего экземпляра ( [temp.dep.type]), и nested-name-specifier относится к зависимому типу, он должен иметь префикс в виде ключевого слова typename, образующего typename-specifier. Если qualified-idв a typename-specifier не обозначен тип или шаблон класса, программа имеет неправильный формат.

typename-specifier:
	typename nested-name-specifier identifier
	typename nested-name-specifier templateopt simple-template-id

Если специализация шаблона создается для набора template-arguments таких, что qualified-id префикс typename не обозначает тип или шаблон класса, специализация сформирована неправильно. Обычный qualified name lookup используется для поиска qualified-id даже при наличии typename. [Example:

struct A {
  struct X { };
  int X;
};
struct B {
  struct X { };
};
template<class T> void f(T t) {
  typename T::X x;
}
void foo() {
  A a;
  B b;
  f(b);             // OK: T​::​X refers to B​::​X
  f(a);             // error: T​::​X refers to the data member A​::​X not the struct A​::​X
}

end example]

Полное имя, используемое в качестве имени в a class-or-decltype или an elaborated-type-specifier , неявно предполагается для наименования типа без использования typename ключевого слова. В, nested-name-specifierкоторый непосредственно содержит, nested-name-specifier который зависит от параметра шаблона, неявно предполагается , что identifierили simple-template-idдля имени типа, без использования typename ключевого слова. [ Ключевое слово не допускается синтаксисом этих конструкций. ]Note: typenameend note

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

template <class T> void f(int i) {
  T::x * i;         // T​::​x must not be a type
}

struct Foo {
  typedef int x;
};

struct Bar {
  static int const x = 5;
};

int main() {
  f<Bar>(1);        // OK
  f<Foo>(1);        // error: Foo​::​x is a type
}

end example]

В определении шаблона класса или в определении члена шаблона класса, следующего за declarator-id, ключевое слово typename не требуется при ссылке на имя ранее объявленного члена шаблона класса, который объявляет тип или шаблон класса. [ Note: Такие имена можно найти с помощью функции unqualified name lookupпоиска члена класса ( [class.qual]) в current instantiationвыражении доступа к члену класса или поиска ( [basic.lookup.classref]), когда тип выражения объекта является текущим экземпляром ( [temp.dep.expr]). ] [end noteExample:

template<class T> struct A {
  typedef int B;
  B b;              // OK, no typename required
};

end example]

Зная, какие имена являются именами типов, можно проверить синтаксис каждого шаблона. Программа составлена ​​неправильно, диагностика не требуется, если:

  • никакая допустимая специализация не может быть сгенерирована для шаблона или части constexpr if оператора в шаблоне, и шаблон не создается, или

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

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

  • интерпретация такой конструкции в гипотетическом экземпляре отличается от интерпретации соответствующей конструкции в любом фактическом создании экземпляра шаблона. [ Note: Это может произойти в следующих ситуациях:

    • тип, используемый в независимом имени, является неполным в точке, в которой определен шаблон, но завершен в точке, в которой выполняется создание экземпляра, или

    • поиск имени в определении шаблона нашел a using-declaration, но поиск в соответствующей области в экземпляре не нашел никаких объявлений, потому что это using-declaration было расширение пакета, а соответствующий пакет пуст, или

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

    • constant expression evaluation в экземпляре шаблона использует

      • значение const объекта целочисленного или перечислимого типа без области видимости, или

      • стоимость constexpr объекта или

      • значение ссылки или

      • определение функции constexpr,

      и этот объект не был определен при определении шаблона, или

    • специализация шаблона класса или специализация шаблона переменной, которая указана независимым simple-template-id, используется шаблоном, и либо он создается из частичной специализации, которая не была определена при определении шаблона, либо он называет явную специализацию, которая не была объявлена когда шаблон был определен.

    end note]

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

int j;
template<class T> class X {
  void f(T t, int i, char* p) {
    t = i;          // diagnosed if X​::​f is instantiated, and the assignment to t is an error
    p = i;          // may be diagnosed even if X​::​f is not instantiated
    p = j;          // may be diagnosed even if X​::​f is not instantiated
  }
  void g(T t) {
    +;              // may be diagnosed even if X​::​g is not instantiated
  }
};

template<class... T> struct A {
  void operator++(int, T... t);                     // error: too many parameters
};
template<class... T> union X : T... { };            // error: union with base class
template<class... T> struct A : T...,  T... { };    // error: duplicate base class

end example]

При поиске объявления имени, используемого в определении шаблона, обычные правила поиска ( [basic.lookup.unqual], [basic.lookup.argdep]) используются для независимых имен. Поиск имен, зависящих от параметров шаблона, откладывается до тех пор, пока не станет известен фактический аргумент шаблона ( [temp.dep]). [Example:

#include <iostream>
using namespace std;

template<class T> class Set {
  T* p;
  int cnt;
public:
  Set();
  Set<T>(const Set<T>&);
  void printall() {
    for (int i = 0; i<cnt; i++)
      cout << p[i] << '\n';
  }
};

в примере i - это локальная переменная, i объявленная в printall, cnt это член, cnt объявленный в Set, и cout это стандартный поток вывода, объявленный в iostream. Однако не все объявления можно найти таким образом; разрешение некоторых имен должно быть отложено до тех пор, пока не template-arguments станут известны действительные . Например, даже несмотря на то, что имя operator<< известно в определении, printall() и его объявление можно найти в <iostream>, фактическое объявление, operator<< необходимое для печати, p[i] не может быть известно, пока не станет известно, что это за тип T ( [temp.dep]). ]end example

Если имя не зависит от template-parameter (как определено в [temp.dep]), объявление (или набор объявлений) для этого имени должно быть в области видимости в точке, где имя появляется в определении шаблона; имя привязано к объявлению (или объявлениям), найденному в этой точке, и на эту привязку не влияют объявления, видимые в момент создания экземпляра. [Example:

void f(char);

template<class T> void g(T t) {
  f(1);             // f(char)
  f(T(1));          // dependent
  f(t);             // dependent
  dd++;             // not dependent; error: declaration for dd not found
}

enum E { e };
void f(E);

double dd;
void h() {
  g(e);             // will cause one call of f(char) followed by two calls of f(E)
  g('a');           // will cause three calls of f(char)
}

end example]

[ Note: В целях поиска имени аргументы по умолчанию, а также noexcept-specifiers шаблоны функций, аргументы по умолчанию и noexcept-specifiers функции-члены шаблонов классов считаются определениями ( [temp.decls]). ]end note

17.6.1 Locally declared names [temp.local]

Как и обычные (не шаблонные) классы, шаблоны классов имеют расширение injected-class-name. Введенное-имя-класса может использоваться как template-nameили type-name. Когда он используется с template-argument-list, как template-argumentдля шаблона template-parameterили как окончательный идентификатор в elaborated-type-specifierобъявлении шаблона дружественного класса, он ссылается на сам шаблон класса. В противном случае, это равносильно тому , template-name сопровождаемое template-parameters шаблон класса , заключенном в <>.

В рамках специализации шаблона класса или частичной специализации, когда имя injected-class-name используется как a type-name, оно эквивалентно, за template-nameкоторым следует template-arguments специализация шаблона класса или частичная специализация, заключенная в <>. [Example:

template<template<class> class T> class A { };
template<class T> class Y;
template<> class Y<int> {
  Y* p;                               // meaning Y<int>
  Y<char>* q;                         // meaning Y<char>
  A<Y>* a;                            // meaning A<​::​Y>
  class B {
    template<class> friend class Y;   // meaning ​::​Y
  };
};

end example]

Нагнетаемого класса имя шаблона класса или класса специализации шаблона может быть использован либо как template-nameили type-name там , где она находится в области видимости. [Example:

template <class T> struct Base {
  Base* p;
};

template <class T> struct Derived: public Base<T> {
  typename Derived::Base* p;    // meaning Derived​::​Base<T>
};

template<class T, template<class> class U = T::template Base> struct Third { };
Third<Base<int> > t;            // OK: default argument uses injected-class-name as a template

end example]

Поиск, который находит внедренное имя-класса ( [class.member.lookup]), в некоторых случаях может привести к неоднозначности (например, если он найден более чем в одном базовом классе). Если все найденные имена внедренных классов относятся к специализациям одного и того же шаблона класса, и если имя используется как a template-name, ссылка относится к самому шаблону класса, а не к его специализации, и не является двусмысленной. [Example:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
  typename Derived::Base b;             // error: ambiguous
  typename Derived::Base<double> d;     // OK
};

end example]

Когда используется обычное имя шаблона (т. Е. Имя из охватывающей области, а не внедренное имя-класса), оно всегда относится к самому шаблону класса, а не к его специализации. [Example:

template<class T> class X {
  X* p;             // meaning X<T>
  X<T>* p2;
  X<int>* p3;
  ::X* p4;          // error: missing template argument list
                    // ​::​X does not refer to the injected-class-name
};

end example]

A template-parameter не следует повторно объявлять в пределах своей области действия (включая вложенные области действия). Имя A template-parameter не должно совпадать с именем шаблона. [Example:

template<class T, int i> class Y {
  int T;            // error: template-parameter redeclared
  void f() {
    char T;         // error: template-parameter redeclared
  }
};

template<class X> class X;      // error: template-parameter redeclared

end example]

В определении члена шаблона класса, который появляется вне определения шаблона класса, имя члена шаблона класса скрывает имя template-parameter любого включающего шаблона класса (но не template-parameterчлена, если член является шаблон класса или функции). [Example:

template<class T> struct A {
  struct B { /* ... */ };
  typedef void C;
  void f();
  template<class U> void g(U);
};

template<class B> void A<B>::f() {
  B b;              // A's B, not the template parameter
}

template<class B> template<class C> void A<B>::g(C) {
  B b;              // A's B, not the template parameter
  C c;              // the template parameter C, not A's C
}

end example]

В определении члена шаблона класса, который появляется за пределами пространства имен, содержащего определение шаблона класса, имя template-parameter скрывает имя члена этого пространства имен. [Example:

namespace N {
  class C { };
  template<class T> class B {
    void f(T);
  };
}
template<class C> void N::B<C>::f(C) {
  C b;              // C is the template parameter, not N​::​C
}

end example]

В определении шаблона класса или в определении члена такого шаблона, который появляется вне определения шаблона, для каждого независимого базового класса ( [temp.dep.type]), если имя базового класса или имя члена базовый класс такой же , как название template-parameter, имя базового класса или члена имя hides на template-parameter имя. [Example:

struct A {
  struct B { /* ... */ };
  int a;
  int Y;
};

template<class B, class a> struct X : A {
  B b;              // A's B
  a b;              // error: A's a isn't a type name
};

end example]

17.6.2 Dependent names [temp.dep]

Внутри шаблона некоторые конструкции имеют семантику, которая может отличаться от одного экземпляра к другому. Такая конструкция зависит от параметров шаблона. В частности, типы и выражения могут зависеть от типа и / или значения параметров шаблона (как определено аргументами шаблона), и это определяет контекст для поиска имени для определенных имен. Выражения могут быть type-dependent (то есть его тип может зависеть от параметра шаблона) или value-dependent (то есть его значение при оценке как постоянное выражение ( [expr.const]) может зависеть от параметра шаблона), как описано в этом подпункте. В выражении формы:

postfix-expression ( expression-listopt )

где postfix-expression является an unqualified-id, unqualified-id обозначает a, dependent name если

Если операнд оператора является выражением, зависящим от типа, оператор также обозначает зависимое имя. Такие имена не связаны и просматриваются в точке создания экземпляра шаблона ( [temp.point]) как в контексте определения шаблона, так и в контексте точки создания экземпляра.

[Example:

template<class T> struct X : B<T> {
  typename T::A* pa;
  void f(B<T>* pb) {
    static int i = B<T>::i;
    pb->j++;
  }
};

имя базового класса, имя B<T>типа T​::​A, имена B<T>​::​i и pb->j явно зависят от template-parameter. ]end example

В определении класса или шаблона класса область видимости dependent base class не проверяется во время неквалифицированного поиска имени ни в точке определения шаблона или члена класса, ни во время создания экземпляра шаблона или члена класса. [Example:

typedef double A;
template<class T> class B {
  typedef int A;
};
template<class T> struct X : B<T> {
  A a;              // a has type double
};

Имя типа A в определении X<T> связывается с именем typedef, определенным в области глобального пространства имен, а не с именем typedef, определенным в базовом классе B<T>. ] [end exampleExample:

struct A {
  struct B { /* ... */ };
  int a;
  int Y;
};

int a;

template<class T> struct Y : T {
  struct B { /* ... */ };
  B b;                          // The B defined in Y
  void f(int i) { a = i; }      // ​::​a
  Y* p;                         // Y<T>
};

Y<A> ya;

Члены A​::​B, A​::​aи A​::​Y аргумента шаблона A не влияют на привязку имен в Y<A>. ]end example

17.6.2.1 Dependent types [temp.dep.type]

Имя относится к тому, current instantiation если это

  • в определении шаблона класса, вложенного класса шаблона класса, члена шаблона класса или члена вложенного класса шаблона injected-class-name класса, шаблона класса или вложенного класса,

  • в определении шаблона первичного класса или члена шаблона первичного класса, имя шаблона класса, за которым следует список аргументов шаблона первичного шаблона (как описано ниже), заключенный в <> (или эквивалентную специализацию псевдонима шаблона),

  • в определении вложенного класса шаблона класса имя вложенного класса, на который ссылается как на член текущего экземпляра, или

  • в определении частичной специализации или члена частичной специализации - имя шаблона класса, за которым следует список аргументов шаблона частичной специализации, заключенной в <> (или эквивалентная специализация псевдонима шаблона). Если параметр nth шаблона является пакетом параметров, nаргумент шаблона th является pack expansion шаблоном, шаблон которого является именем пакета параметров.

Список аргументов шаблона первичного шаблона - это список аргументов шаблона, в котором nаргумент th шаблона имеет значение параметра nth шаблона шаблона класса. Если nth параметром шаблона является a template parameter pack, nаргумент th шаблона - это a pack expansion , шаблон которого является именем пакета параметров шаблона.

Аргумент шаблона, который эквивалентен параметру шаблона (т. Е. Имеет то же постоянное значение или тот же тип, что и параметр шаблона), может использоваться вместо этого параметра шаблона в ссылке на текущий экземпляр. В случае аргумента шаблона, не являющегося типом, аргументу должно быть задано значение параметра шаблона, а не выражение, в котором параметр шаблона появляется как часть выражения. [Example:

template <class T> class A {
  A* p1;                        // A is the current instantiation
  A<T>* p2;                     // A<T> is the current instantiation
  A<T*> p3;                     // A<T*> is not the current instantiation
  ::A<T>* p4;                   // ​::​A<T> is the current instantiation
  class B {
    B* p1;                      // B is the current instantiation
    A<T>::B* p2;                // A<T>​::​B is the current instantiation
    typename A<T*>::B* p3;      // A<T*>​::​B is not the current instantiation
  };
};

template <class T> class A<T*> {
  A<T*>* p1;                    // A<T*> is the current instantiation
  A<T>* p2;                     // A<T> is not the current instantiation
};

template <class T1, class T2, int I> struct B {
  B<T1, T2, I>* b1;             // refers to the current instantiation
  B<T2, T1, I>* b2;             // not the current instantiation
  typedef T1 my_T1;
  static const int my_I = I;
  static const int my_I2 = I+0;
  static const int my_I3 = my_I;
  B<my_T1, T2, my_I>* b3;       // refers to the current instantiation
  B<my_T1, T2, my_I2>* b4;      // not the current instantiation
  B<my_T1, T2, my_I3>* b5;      // refers to the current instantiation
};

end example]

A dependent base class - это базовый класс, который является зависимым типом и не является текущим экземпляром. [ Note: Базовый класс может быть текущим экземпляром в случае, если вложенный класс называет включающий класс базовым. [Example:

template<class T> struct A {
  typedef int M;
  struct B {
    typedef void M;
    struct C;
  };
};

template<class T> struct A<T>::B::C : A<T> {
  M m;                          // OK, A<T>​::​M
};

end example] ]end note

Имя - это member of the current instantiation если это

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

  • A, qualified-id в котором nested-name-specifier относится к текущему экземпляру и который при поиске относится по крайней мере к одному члену класса, который является текущим экземпляром, или его независимым базовым классом. [ Note: Если такой член не найден, а у текущего экземпляра есть какие-либо зависимые базовые классы, то qualified-idэто член неизвестной специализации; см. ниже. ] end note

  • id-expressionОбозначая член в class member access выражении для которого тип выражения объекта является текущим конкретизации, и id-expression, когда looked up, относится к по меньшей мере , одного члена класса , который является текущим конкретизации или не зависящих от их базового класса. [ Note: Если такой член не найден, а у текущего экземпляра есть какие-либо зависимые базовые классы, то id-expressionэто член неизвестной специализации; см. ниже. ] end note

[Example:

template <class T> class A {
  static const int i = 5;
  int n1[i];                    // i refers to a member of the current instantiation
  int n2[A::i];                 // A​::​i refers to a member of the current instantiation
  int n3[A<T>::i];              // A<T>​::​i refers to a member of the current instantiation
  int f();
};

template <class T> int A<T>::f() {
  return i;                     // i refers to a member of the current instantiation
}

end example]

Имя - это, dependent member of the current instantiation если он является членом текущего экземпляра, который при поиске ссылается по крайней мере на один член класса, который является текущим экземпляром.

Имя - это member of an unknown specialization если это

  • Объект, qualified-id в котором nested-name-specifier именуется зависимый тип, не являющийся текущим экземпляром.

  • A, qualified-idв котором nested-name-specifier относится к текущему экземпляру, текущий экземпляр имеет по крайней мере один зависимый базовый класс, а поиск имени qualified-idне находит ни одного члена класса, который является текущим экземпляром или его независимым базовым классом.

  • id-expressionОбозначая член в class member access выражении , в котором либо

    • тип объектного выражения - это текущая реализация, текущая реализация имеет по крайней мере один зависимый базовый класс, а поиск имени id-expressionне находит члена класса, который является текущим экземпляром, или его независимого базового класса; или

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

Если a, qualified-idв котором nested-name-specifier относится к текущему экземпляру, не является членом текущего экземпляра или элементом неизвестной специализации, программа имеет неправильный формат, даже если шаблон, содержащий элемент, не создается qualified-id; диагностика не требуется. Точно так же, если id-expressionвыражение доступа к члену класса, для которого тип выражения объекта является текущим экземпляром, не относится к члену текущего экземпляра или члену неизвестной специализации, программа имеет неправильный формат, даже если шаблон содержащее выражение доступа к члену не создается; диагностика не требуется. [Example:

template<class T> class A {
  typedef int type;
  void f() {
    A<T>::type i;               // OK: refers to a member of the current instantiation
    typename A<T>::other j;     // error: neither a member of the current instantiation nor
                                // a member of an unknown specialization
  }
};

end example]

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

struct A {
  int m;
};

struct B {
  int m;
};

template<typename T>
struct C : A, T {
  int f() { return this->m; }   // finds A​::​m in the template definition context
  int g() { return m; }         // finds A​::​m in the template definition context
};

template int C<B>::f();     // error: finds both A​::​m and B​::​m
template int C<B>::g();     // OK: transformation to class member access syntax
                            // does not occur in the template definition context; see [class.mfct.non-static]

end example]

Тип является зависимым, если он

  • параметр шаблона,

  • сотрудник неизвестной специализации,

  • вложенный класс или перечисление, которое является зависимым членом текущего экземпляра,

  • тип cv-qualified, где cv-unqualified тип является зависимым,

  • составной тип, созданный из любого зависимого типа,

  • тип массива, тип элемента которого является зависимым или чья граница (если есть) зависит от значения,

  • тип функции, спецификация исключения которой зависит от значения,

  • a, simple-template-id в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым типом или выражением, которое зависит от типа или значения, или является расширением пакета [ Note: Сюда входит injected-class-name шаблон класса, используемый без расширения template-argument-list. ] , или end note

  • обозначается , где есть . decltype(expression)expression type-dependent

[ Note: Поскольку typedef не вводит новые типы, а вместо этого просто ссылается на другие типы, имя, которое ссылается на typedef, который является членом текущего экземпляра, зависит только в том случае, если тип, на который ссылается, является зависимым. ]end note

17.6.2.2 Type-dependent expressions [temp.dep.expr]

За исключением случаев, описанных ниже, выражение зависит от типа, если какое-либо подвыражение зависит от типа.

this зависит от типа, если тип класса включающей функции-члена зависит ( [temp.dep.type]).

Зависит от id-expression типа, если он содержит

  • identifier , связанный с именем поиска с одним или более деклараций объявлен с зависимым типом,

  • identifier , связанный с именем поиска с не-типа template-parameter объявлена с типом , который содержит placeholder type,

  • identifier, связанный с именем поиска с одним или более деклараций функций членов текущего экземпляра объявленных с типом возвращаемого , который содержит тип заполнителя,

  • identifier, связанный с именем поиском с structured binding declaration которого brace-or-equal-initializerявляется типом-зависимым,

  • identifier _­_­func_­_­, где любая вмещающих функция представляет собой шаблон, член шаблона класса, или общая лямбду,

  • template-id , что зависит,

  • a, conversion-function-id который указывает зависимый тип, или

  • a nested-name-specifier или a, в qualified-id котором упоминается член неизвестной специализации;

или если он называет зависимый член текущего экземпляра, который является статическим элементом данных типа «массив неизвестных границ T» для some T ( [temp.static]). Выражения следующих форм зависят от типа, только если тип, указанный с помощью type-id, simple-type-specifier или new-type-id является зависимым, даже если любое подвыражение зависит от типа:

simple-type-specifier ( expression-listopt )
::opt new new-placementopt new-type-id new-initializeropt
::opt new new-placementopt ( type-id ) new-initializeropt
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
( type-id ) cast-expression

Выражения следующих форм никогда не зависят от типа (поскольку тип выражения не может быть зависимым):

literal
postfix-expression . pseudo-destructor-name
postfix-expression -> pseudo-destructor-name
sizeof unary-expression
sizeof ( type-id )
sizeof ... ( identifier )
alignof ( type-id )
typeid ( expression )
typeid ( type-id )
::opt delete cast-expression
::opt delete [ ] cast-expression
throw assignment-expressionopt
noexcept ( expression )

[ Note: Для стандартного библиотечного макроса offsetofсм [support.types].. ]end note

A class member access expression зависит от типа, если выражение относится к члену текущего экземпляра, а тип указанного члена является зависимым, или если выражение доступа к члену класса относится к члену неизвестной специализации. [ Note: В выражении формы x.y или xp->y типа выражения обычно является тип члена y класса x (или класса, на который указывает xp). Однако, если x или xp относится к зависимому типу, который не является текущим экземпляром, тип y всегда является зависимым. Если x или xp относится к независимому типу или относится к текущему экземпляру, тип y является типом выражения доступа к члену класса. ]end note

A braced-init-listзависит от типа, если какой-либо элемент зависит от типа или является расширением пакета.

A fold-expressionзависит от типа.

17.6.2.3 Value-dependent expressions [temp.dep.constexpr]

За исключением описанного ниже, выражение, используемое в контексте, где требуется постоянное выражение, зависит от значения, если какое-либо подвыражение зависит от значения.

An id-expression зависит от значения, если:

  • это зависит от типа,

  • это имя параметра шаблона, не являющегося типом,

  • он называет статический член данных, который является зависимым членом текущего экземпляра и не инициализируется в a member-declarator,

  • он называет статическую функцию-член, которая является зависимым членом текущего экземпляра, или

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

Выражения следующей формы зависят от значения, если unary-expressionили expression зависит от типа, или type-id зависит:

sizeof unary-expression
sizeof ( type-id )
typeid ( expression )
typeid ( type-id )
alignof ( type-id )
noexcept ( expression )

[ Note: Для стандартного библиотечного макроса offsetofсм [support.types].. ]end note

Выражения следующей формы являются значением-зависимой , если либо type-id или simple-type-specifier зависит , или , expression или cast-expression является значением в зависимости от:

simple-type-specifier ( expression-listopt )
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
( type-id ) cast-expression

Выражения следующей формы зависят от значения:

sizeof ... ( identifier )
fold-expression

Выражение формы, в которой имена зависимых членов текущего экземпляра зависят от значения. Выражение вида также является значением-зависимым , если оценивать как успешно , и результат оценки относится к шаблонному объекту , который является объектом с длительностью статической или потоком хранения или функцией - члена. &qualified-idqualified-id &cast-expressioncast-expression core constant expression

17.6.2.4 Dependent template arguments [temp.dep.temp]

Тип template-argument является зависимым, если тип, который он указывает, является зависимым.

Не-тип template-argument является зависимым, если его тип является зависимым или константное выражение, которое он задает, зависит от значения.

Кроме того, не-тип template-argument является зависимым, если соответствующий не-тип template-parameter относится к типу ссылки или указателя, а template-argument обозначает или указывает на член текущего экземпляра или член зависимого типа.

Шаблон template-argument является зависимым, если он называет template-parameter или qualified-id относится к члену неизвестной специализации.

17.6.3 Non-dependent names [temp.nondep]

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

void g(double);
void h();

template<class T> class Z {
public:
  void f() {
    g(1);           // calls g(double)
    h++;            // ill-formed: cannot increment function; this could be diagnosed
                    // either here or at the point of instantiation
  }
};

void g(int);        // not in scope at the point of the template definition, not considered for the call g(1)

end example]

17.6.4 Dependent name resolution [temp.dep.res]

При разрешении зависимых имен учитываются имена из следующих источников:

  • Объявления, которые видны в момент определения шаблона.

  • Объявления из пространств имен, связанных с типами аргументов функции, как из контекста создания ( [temp.point]), так и из контекста определения.

17.6.4.1 Point of instantiation [temp.point]

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

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

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

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

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

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

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

Специализация для шаблона функции, шаблона функции-члена или функции-члена или статического члена данных шаблона класса может иметь несколько точек создания экземпляров в единице перевода, и в дополнение к точкам создания экземпляров, описанным выше, для любых таких специализации, которая имеет точку создания в пределах единицы перевода, конец единицы трансляции также считается точкой создания экземпляра. Специализация для шаблона класса имеет не более одной точки создания экземпляра в единице перевода. Специализация для любого шаблона может иметь точки реализации в нескольких единицах перевода. Если две разные точки создания экземпляра придают специализации шаблона разные значения в зависимости от one-definition rule, программа имеет неправильный формат и диагностика не требуется.

17.6.4.2 Candidate functions [temp.dep.candidate]

Для вызова функции, где postfix-expression- зависимое имя, функции-кандидаты находятся с использованием обычных правил поиска ( [basic.lookup.unqual], [basic.lookup.argdep]), за исключением того, что:

  • Для части использования поиска unqualified name lookupнайдены только объявления функций из контекста определения шаблона.

  • Для части поиска с использованием связанных пространств имен ( [basic.lookup.argdep]) обнаруживаются только объявления функций, найденные либо в контексте определения шаблона, либо в контексте создания экземпляра шаблона.

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

17.6.5 Friend names declared within a class template [temp.inject]

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

Как и в случае с классами, не являющимися шаблонами, имена дружественных функций области видимости пространства имен специализации шаблона класса не видны во время обычного поиска, если явно не объявлены в области видимости пространства имен ( [class.friend]). Такие имена можно найти в правилах для связанных классов ( [basic.lookup.argdep]). 140 [Example:

template<typename T> struct number {
  number(int);
  friend number gcd(number x, number y) { return 0; };
};

void g() {
  number<double> a(3), b(4);
  a = gcd(a,b);     // finds gcd because number<double> is an associated class,
                    // making gcd visible in its namespace (global scope)
  b = gcd(3,4);     // ill-formed; gcd is not visible
}

end example]

Объявления друзей не вводят новые имена ни в какую область видимости ни при объявлении шаблона, ни при его создании.

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

17.8 Function template specializations [temp.fct.spec]

Функция, созданная из шаблона функции, называется специализацией шаблона функции; так это явная специализация шаблона функции. Аргументы шаблона могут быть явно указаны при именовании специализации шаблона функции, выведены из контекста (например, выведены из аргументов функции при вызове специализации шаблона функции, см. [temp.deduct]) Или получены из аргументов шаблона по умолчанию.

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

template<class T> void f(T* p) {
  static T s;
};

void g(int a, char* b) {
  f(&a);            // calls f<int>(int*)
  f(&b);            // calls f<char*>(char**)
}

Здесь f<int>(int*) есть статическая переменная s типа int и f<char*>(char**) есть статическая переменная s типа char*. ]end example

17.8.1 Explicit template argument specification [temp.arg.explicit]

Аргументы шаблона могут быть указаны при ссылке на специализацию шаблона функции путем уточнения имени шаблона функции списком template-arguments таким же образом, как template-arguments это указывается при использовании специализации шаблона класса. [Example:

template<class T> void sort(Array<T>& v);
void f(Array<dcomplex>& cv, Array<int>& ci) {
  sort<dcomplex>(cv);                   // sort(Array<dcomplex>&)
  sort<int>(ci);                        // sort(Array<int>&)
}

а также

template<class U, class V> U convert(V v);

void g(double d) {
  int i = convert<int,double>(d);       // int convert(double)
  char c = convert<char,double>(d);     // char convert(double)
}

end example]

Список аргументов шаблона может быть указан при ссылке на специализацию шаблона функции.

  • когда функция вызывается,

  • когда берется адрес функции, когда функция инициализирует ссылку на функцию или когда формируется указатель на функцию-член,

  • в явной специализации,

  • в явном экземпляре, или

  • в объявлении друга.

Конечные аргументы шаблона, которые могут быть deduced или получены по умолчанию, template-arguments могут быть исключены из списка явных template-arguments. Завершающий, template parameter pack не выведенный иным образом, будет выведен в пустую последовательность аргументов шаблона. Если все аргументы шаблона могут быть выведены, все они могут быть опущены; в этом случае сам пустой список аргументов шаблона <> также может быть опущен. В контекстах, где дедукция выполняется и терпит неудачу, или в контекстах, где дедукция не выполняется, если указан список аргументов шаблона и он, вместе с любыми аргументами шаблона по умолчанию, идентифицирует одну специализацию шаблона функции, тогда template-id это lvalue для функции специализация шаблона. [Example:

template<class X, class Y> X f(Y);
template<class X, class Y, class ... Z> X g(Y);
void h() {
  int i = f<int>(5.6);          // Y is deduced to be double
  int j = f(5.6);               // ill-formed: X cannot be deduced
  f<void>(f<int, bool>);        // Y for outer f deduced to be int (*)(bool)
  f<void>(f<int>);              // ill-formed: f<int> does not denote a single function template specialization
  int k = g<int>(5.6);          // Y is deduced to be double, Z is deduced to an empty sequence
  f<void>(g<int, bool>);        // Y for outer f is deduced to be int (*)(bool),
                                // Z is deduced to an empty sequence
}

end example]

[ Note: Пустой список аргументов шаблона может использоваться, чтобы указать, что данное использование относится к специализации шаблона функции, даже если [dcl.fct]видна не являющаяся шаблоном функция ( ), которая в противном случае использовалась бы. Например:

template <class T> int f(T);    // #1
int f(int);                     // #2
int k = f(1);                   // uses #2
int l = f<>(1);                 // uses #1

end note]

Присутствующие аргументы шаблона должны быть указаны в порядке объявления их соответствующих template-parameters. Список аргументов шаблона не должен указывать больше, template-arguments чем есть соответствующие, template-parameters если один из них не template-parameters является пакетом параметров шаблона. [Example:

template<class X, class Y, class Z> X f(Y,Z);
template<class ... Args> void f2();
void g() {
  f<int,const char*,double>("aa",3.0);
  f<int,const char*>("aa",3.0); // Z is deduced to be double
  f<int>("aa",3.0);             // Y is deduced to be const char*, and Z is deduced to be double
  f("aa",3.0);                  // error: X cannot be deduced
  f2<char, short, int, long>(); // OK
}

end example]

Неявные преобразования (Clause [conv]) будут выполнены для аргумента функции, чтобы преобразовать его в тип соответствующего параметра функции, если тип параметра не содержит никаких, template-parameters которые участвуют в выводе аргумента шаблона. [ Note: Параметры шаблона не участвуют в выводе аргументов шаблона, если они явно указаны. Например,

template<class T> void f(T);

class Complex {
  Complex(double);
};

void g() {
  f<Complex>(1);    // OK, means f<Complex>(Complex(1))
}

end note]

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

[ Note: Для простых имен функций argument dependent lookup применяется, даже если имя функции не отображается в рамках вызова. Это потому, что вызов по-прежнему имеет синтаксическую форму вызова функции ( [basic.lookup.unqual]). Но когда используется шаблон функции с явными аргументами шаблона, вызов не имеет правильной синтаксической формы, если не существует шаблона функции с таким именем, видимого в точке вызова. Если такое имя не отображается, вызов не имеет синтаксической правильной формы и поиск, зависящий от аргументов, не применяется. Если такое имя видно, применяется поиск, зависящий от аргументов, и дополнительные шаблоны функций могут быть найдены в других пространствах имен. [Example:

namespace A {
  struct B { };
  template<int X> void f(B);
}
namespace C {
  template<class T> void f(T t);
}
void g(A::B b) {
  f<3>(b);          // ill-formed: not a function call
  A::f<3>(b);       // well-formed
  C::f<3>(b);       // ill-formed; argument dependent lookup applies only to unqualified names
  using C::f;
  f<3>(b);          // well-formed because C​::​f is visible; then A​::​f is found by argument dependent lookup
}

end example] ]end note

Вывод аргументов шаблона может расширить последовательность аргументов шаблона, соответствующую пакету параметров шаблона, даже если последовательность содержит явно указанные аргументы шаблона. [Example:

template<class ... Types> void f(Types ... values);

void g() {
  f<int*, float*>(0, 0, 0);     // Types is deduced to the sequence int*, float*, int
}

end example]

17.8.2 Template argument deduction [temp.deduct]

При ссылке на специализацию шаблона функции все аргументы шаблона должны иметь значения. Значения могут быть явно указаны или, в некоторых случаях, выведены из использования или получены по умолчанию template-arguments. [Example:

void f(Array<dcomplex>& cv, Array<int>& ci) {
  sort(cv);                     // calls sort(Array<dcomplex>&)
  sort(ci);                     // calls sort(Array<int>&)
}

а также

void g(double d) {
  int i = convert<int>(d);      // calls convert<int,double>(double)
  int c = convert<char>(d);     // calls convert<char,double>(double)
}

end example]

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

  • Указанные аргументы шаблона должны соответствовать параметрам шаблона в натуральном выражении (т. Е. Тип, не тип, шаблон). Аргументов не должно быть больше, чем параметров, если хотя бы один параметр не является пакетом параметров шаблона, и должен быть аргумент для каждого параметра, не являющегося пакетом. В противном случае определение типа не удастся.

  • Аргументы, не являющиеся типами, должны соответствовать типам соответствующих параметров шаблона, не являющихся типами, или должны быть преобразованы в типы соответствующих параметров, не являющихся типами, как указано в [temp.arg.nontype], в противном случае определение типа завершится ошибкой.

  • Указанные значения аргументов шаблона заменяются соответствующими параметрами шаблона, как указано ниже.

После выполнения этой замены выполняются настройки типа параметра функции, описанные в [dcl.fct] . [ Example: Тип параметра « void (const int, int[5])» становится « void(*)(int,int*)». ] [ Квалификатор верхнего уровня в объявлении параметра функции не влияет на тип функции, но по-прежнему влияет на тип переменной параметра функции внутри функции. ] [end exampleNote: end noteExample:

template <class T> void f(T t);
template <class X> void g(const X x);
template <class Z> void h(Z, Z*);

int main() {
  // #1: function type is f(int), t is non const
  f<int>(1);

  // #2: function type is f(int), t is const
  f<const int>(1);

  // #3: function type is g(int), x is const
  g<int>(1);

  // #4: function type is g(int), x is const
  g<const int>(1);

  // #5: function type is h(int, const int*)
  h<const int>(1,0);
}

end example]

[ Note: f<int>(1) и f<const int>(1) вызывают разные функции, даже если обе вызываемые функции имеют один и тот же тип функции. ]end note

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

template <class T, class U = double>
void f(T t = 0, U u = 0);

void g() {
  f(1, 'c');        // f<int,char>(1,'c')
  f(1);             // f<int,double>(1,0)
  f();              // error: T cannot be deduced
  f<int>();         // f<int,double>(0,0)
  f<int,char>();    // f<int,char>(0,0)
}

end example]

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

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

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

template <class T> struct A { using X = typename T::X; };
template <class T> typename T::X f(typename A<T>::X);
template <class T> void f(...) { }
template <class T> auto g(typename A<T>::X) -> typename T::X;
template <class T> void g(...) { }

void h() {
  f<int>(0);        // OK, substituting return type causes deduction to fail
  g<int>(0);        // error, substituting parameter type instantiates A<int>
}

end example]

Если подстановка приводит к недопустимому типу или выражению, определение типа не выполняется. Недопустимый тип или выражение - это тот, который был бы неправильно сформирован и потребовал бы диагностики, если бы он был записан с использованием подставленных аргументов. [ Note: Если диагностика не требуется, программа все еще плохо сформирована. Проверка доступа выполняется как часть процесса замены. ] Только недопустимые типы и выражения в непосредственном контексте типа функции и его типов параметров шаблона могут привести к сбою вывода. [ Подстановка типов и выражений может привести к таким эффектам, как создание экземпляров специализаций шаблонов классов и / или специализаций шаблонов функций, создание неявно определенных функций и т. Д. Такие эффекты не находятся в «непосредственном контексте» и могут привести к программа плохо сформирована. ] end noteNote: end note

[Example:

struct X { };
struct Y {
  Y(X){}
};

template <class T> auto f(T t1, T t2) -> decltype(t1 + t2);     // #1
X f(Y, Y);          // #2

X x1, x2;
X x3 = f(x1, x2);   // deduction fails on #1 (cannot add X+X), calls #2

end example]

[ Note: Вычет типа может не работать по следующим причинам:

  • Попытка создать экземпляр расширения пакета, содержащего несколько пакетов параметров разной длины.

  • Попытка создать массив с типом элемента, то есть voidтипом функции, типом ссылки или типом абстрактного класса, или попытка создать массив с нулевым или отрицательным размером. [Example:

    template <class T> int f(T[5]);
    int I = f<int>(0);
    int j = f<void>(0);             // invalid array
    

    end example]

  • Попытка использовать тип, который не является типом класса или перечисления, в квалифицированном имени. [Example:

    template <class T> int f(typename T::B*);
    int i = f<int>(0);

    end example]

  • Попытка использовать тип в a nested-name-specifierиз a, qualified-idкогда этот тип не содержит указанный член, или

    • указанный член не является типом, для которого требуется тип, или

    • указанный член не является шаблоном, в котором шаблон требуется, или

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

    [Example:

    template <int I> struct X { };
    template <template <class T> class> struct Z { };
    template <class T> void f(typename T::Y*){}
    template <class T> void g(X<T::N>*){}
    template <class T> void h(Z<T::template TT>*){}
    struct A {};
    struct B { int Y; };
    struct C {
      typedef int N;
    };
    struct D {
      typedef int TT;
    };
    
    int main() {
      // Deduction fails in each of these cases:
      f<A>(0);          // A does not contain a member Y
      f<B>(0);          // The Y member of B is not a type
      g<C>(0);          // The N member of C is not a non-type
      h<D>(0);          // The TT member of D is not a template
    }

    end example]

  • Попытка создать указатель на ссылочный тип.

  • Попытка создать ссылку на void.

  • Попытка создать «указатель на член T», когда T это не класс. [Example:

    template <class T> int f(int T::*);
    int i = f<int>(0);

    end example]

  • Попытка присвоить недопустимый тип параметру шаблона, не являющемуся типом. [Example:

    template <class T, T> struct S {};
    template <class T> int f(S<T, T()>*);
    struct X {};
    int i0 = f<X>(0);

    end example]

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

    template <class T, T*> int f(int);
    int i2 = f<int,1>(0);           // can't conv 1 to int*
    

    end example]

  • Попытка создать тип функции, в котором параметр имеет тип voidили возвращаемый тип является типом функции или типом массива.

  • Попытка создать тип функции, в котором тип параметра или возвращаемый тип - это abstract class type.

end note]

[ Example: В следующем примере, предполагая, что a signed char не может представлять значение 1000, a narrowing conversion потребуется для преобразования template-argument типа int в signed char, поэтому подстановка не выполняется для второго шаблона ( [temp.arg.nontype]).

template <int> int f(int);
template <signed char> int f(int);
int i1 = f<1000>(0);            // OK
int i2 = f<1>(0);               // ambiguous; not narrowing

end example]

17.8.2.1 Deducing template arguments from a function call [temp.deduct.call]

Вывод аргументов шаблона выполняется путем сравнения каждого типа параметра шаблона функции (вызовите его P), который содержит, template-parameters который участвует в выводе аргумента шаблона, с типом соответствующего аргумента вызова (вызовите его A), как описано ниже. Если удаление ссылок и cv-квалификаторов из P дает std​::​initializer_­list<P'> или P'[N] для некоторых P' и, N а аргумент является непустым списком инициализатора ( [dcl.init.list]), то вместо этого выполняется вывод для каждого элемента списка инициализаторов, принимая P' в качестве типа параметра шаблона функции и элемент инициализатора в качестве аргумента, а в P'[N] случае, если N это не типовой параметр шаблона, N выводится из длины списка инициализаторов. В противном случае аргумент списка инициализаторов заставляет параметр считаться невыведенным context ( [temp.deduct.type]). [Example:

template<class T> void f(std::initializer_list<T>);
f({1,2,3});                     // T deduced to int
f({1,"asdf"});                  // error: T deduced to both int and const char*

template<class T> void g(T);
g({1,2,3});                     // error: no argument deduced for T

template<class T, int N> void h(T const(&)[N]);
h({1,2,3});                     // T deduced to int, N deduced to 3

template<class T> void j(T const(&)[3]);
j({42});                        // T deduced to int, array bound not considered

struct Aggr { int i; int j; };
template<int N> void k(Aggr const(&)[N]);
k({1,2,3});                     // error: deduction fails, no conversion from int to Aggr
k({{1},{2},{3}});               // OK, N deduced to 3

template<int M, int N> void m(int const(&)[M][N]);
m({{1,2},{3,4}});               // M and N both deduced to 2

template<class T, int N> void n(T const(&)[N], T);
n({{1},{2},{3}},Aggr());        // OK, T is Aggr, N is 3

end example] Для параметра пакета функции , которое происходит в конце parameter-declaration-list, удержание выполняется для каждого оставшегося аргумента вызова, принимая тип P из declarator-idпараметра функции пакета в качестве соответствующего шаблона функции типа параметра. Каждый вывод выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции. Когда пакет параметров функции появляется в невыведенном контексте ( [temp.deduct.type]), тип этого пакета параметров никогда не выводится. [Example:

template<class ... Types> void f(Types& ...);
template<class T1, class ... Types> void g(T1, Types ...);
template<class T1, class ... Types> void g1(Types ..., T1);

void h(int x, float& y) {
  const int z = x;
  f(x, y, z);                   // Types is deduced to int, float, const int
  g(x, y, z);                   // T1 is deduced to int; Types is deduced to float, int
  g1(x, y, z);                  // error: Types is not deduced
  g1<int, int, int>(x, y, z);   // OK, no deduction occurs
}

end example]

Если P это не ссылочный тип:

  • Если A это тип массива, то тип указателя, созданный объектом, array-to-pointer standard conversion используется вместо A вывода типа; иначе,

  • Если A это тип функции, тип указателя, созданный с помощью function-to-pointer standard conversion , используется вместо A вывода типа; иначе,

  • Если A это тип с квалификацией cv, квалификаторы cv верхнего уровня Aдля типа игнорируются при выводе типа.

Если P это тип с квалификацией cv, квалификаторы cv верхнего уровня Pдля типа игнорируются при выводе типа. Если P это ссылочный тип, тип, на который ссылается, P используется для вывода типа. [Example:

template<class T> int f(const T&);
int n1 = f(5);                  // calls f<int>(const int&)
const int i = 0;
int n2 = f(i);                  // calls f<int>(const int&)
template <class T> int g(volatile T&);
int n3 = g(i);                  // calls g<const int>(const volatile int&)

end example] A forwarding reference - это ссылка rvalue на параметр шаблона cv-unqualified, который не представляет параметр шаблона шаблона класса (во время вывода аргументов шаблона класса ( [over.match.class.deduct])). Если P это ссылка пересылки, а аргумент - lvalue, тип «lvalue ссылка на A» используется вместо A для вывода типа. [Example:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i);                  // calls f<int&>(int&)
int n2 = f(0);                  // calls f<int>(int&&)
int n3 = g(i);                  // error: would call g<int>(const int&&), which
                                // would bind an rvalue reference to an lvalue

template <class T> struct A {
  template <class U>
    A(T&&, U&&, int*);          // #1: T&& is not a forwarding reference.
                                // U&& is a forwarding reference.
  A(T&&, int*);                 // #2
};

template <class T> A(T&&, int*) -> A<T>;    // #3: T&& is a forwarding reference.

int *ip;
A a{i, 0, ip};                  // error: cannot deduce from #1
A a0{0, 0, ip};                 // uses #1 to deduce A<int> and #1 to initialize
A a2{i, ip};                    // uses #3 to deduce A<int&> and #2 to initialize

end example]

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

  • Если исходный P является ссылочным типом, выведенный A (т. Е. Тип, на который ссылается ссылка) может быть более квалифицированным cv, чем преобразованный A.

  • Преобразованный A может быть другим указателем или указателем на тип члена, который может быть преобразован в выведенный с A помощью function pointer conversion и / или qualification conversion.

  • Если P есть класс и P имеет форму simple-template-id, то преобразованный A может быть производным классом выведенного A. Аналогично, если P это указатель на класс формы simple-template-id, преобразованный A может быть указателем на производный класс, на который указывает выведенный A.

Эти альтернативы рассматриваются только в том случае, если в противном случае определение типа не удалось бы. Если они дают более одного возможного Aвывода, вывод типа не выполняется. [ Note: Если a template-parameter не используется ни в одном из параметров функции шаблона функции или используется только в невыведенном контексте, его соответствие template-argument не может быть выведено из вызова функции и template-argument должно быть явно указано. ]end note

Когда P это тип функции, тип указателя функции или указатель на тип функции-члена:

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

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

[Example:

// Only one function of an overload set matches the call so the function parameter is a deduced context.
template <class T> int f(T (*p)(T));
int g(int);
int g(char);
int i = f(g);       // calls f(int (*)(int))

end example]

[Example:

// Ambiguous deduction causes the second function parameter to be a non-deduced context.
template <class T> int f(T, T (*p)(T));
int g(int);
char g(char);
int i = f(1, g);    // calls f(int, int (*)(int))

end example]

[Example:

// The overload set contains a template, causing the second function parameter to be a non-deduced context.
template <class T> int f(T, T (*p)(T));
char g(char);
template <class T> T g(T);
int i = f(1, g);    // calls f(int, int (*)(int))

end example]

Если вывод успешен для всех параметров, которые содержат, template-parameters которые участвуют в выводе аргументов шаблона, и все аргументы шаблона явно указаны, выведены или получены из аргументов шаблона по умолчанию, оставшиеся параметры затем сравниваются с соответствующими аргументами. Для каждого оставшегося параметра P с типом, который не был зависимым до подстановки любых явно заданных аргументов шаблона, если соответствующий аргумент A не может быть неявно преобразован в P, выведение не выполняется. [ Note: Параметры с зависимыми типами, которые не template-parameters участвуют в выводе аргументов шаблона, и параметры, которые стали независимыми из-за подстановки явно указанных аргументов шаблона, будут проверяться во время разрешения перегрузки. ] [end noteExample:

  template <class T> struct Z {
    typedef typename T::x xx;
  };
  template <class T> typename Z<T>::xx f(void *, T);    // #1
  template <class T> void f(int, T);                    // #2
  struct A {} a;
  int main() {
    f(1, a);        // OK, deduction fails for #1 because there is no conversion from int to void*
  }

end example]

17.8.2.2 Deducing template arguments taking the address of a function template [temp.deduct.funcaddr]

Аргументы шаблона могут быть выведены из типа, указанного при взятии адреса файла overloaded function. Тип функции шаблона функции и указанный тип используются как типы P и A, и вычитание выполняется, как описано в [temp.deduct.type].

В placeholder type возвращаемом типе шаблона функции A - это невыведенный контекст. Если вывод аргумента шаблона для такой функции завершается успешно, тип возвращаемого значения определяется при создании экземпляра тела функции.

17.8.2.3 Deducing conversion function template arguments [temp.deduct.conv]

Шаблон вывод аргумента делается путем сравнения тип возвращаемого шаблона функции преобразования (назовем его P) с типом , который необходим в качестве результата преобразования (назовем его A; см [dcl.init], [over.match.conv]и [over.match.ref] для определения того типа) , как описано в [temp.deduct.type].

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

Если A это не ссылочный тип:

  • Если P это тип массива, то тип указателя, созданный объектом, array-to-pointer standard conversion используется вместо P вывода типа; иначе,

  • Если P это тип функции, тип указателя, созданный с помощью function-to-pointer standard conversion , используется вместо P вывода типа; иначе,

  • Если P это тип с квалификацией cv, квалификаторы cv верхнего уровня Pдля типа игнорируются при выводе типа.

Если A это тип с квалификацией cv, квалификаторы cv верхнего уровня Aдля типа игнорируются при выводе типа. Если A это ссылочный тип, тип, на который ссылается, A используется для вывода типа.

В общем, процесс вывода пытается найти значения аргументов шаблона, которые сделают вывод A идентичным A. Однако есть четыре случая, в которых допускается различие:

  • Если исходный A тип является ссылочным, он A может быть более квалифицированным cv, чем выведенный A (т. Е. Тип, на который ссылается ссылка)

  • Если оригинал A является типом указателя функции, он A может быть «указателем на функцию», даже если выведенным A является «указатель на функцию без исключения».

  • Если оригинал A является указателем на тип функции-члена, он A может быть «указателем на член функции типа», даже если выведенным A является «указатель на член функции типа noexcept».

  • Вывод A может быть другим указателем или указателем на тип члена, в который можно преобразовать A квалификационное преобразование.

Эти альтернативы рассматриваются только в том случае, если в противном случае определение типа не удалось бы. Если они дают более одного возможного Aвывода, вывод типа не выполняется.

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

Если A это тип cv1,0 «указатель на » cv1,n1 «указатель на» cv1,n T1 и P тип cv2,0 «указатель на » cv2,n1 «указатель на» cv2,n T2, то cv-unqualified T1 и T2 используются как типы A и, P соответственно, для вывода типа. [Example:

struct A {
  template <class T> operator T***();
};
A a;
const int * const * const * p1 = a;     // T is deduced as int, not const int

end example]

17.8.2.4 Deducing template arguments during partial ordering [temp.deduct.partial]

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

Для определения частичного упорядочивания используются два набора типов. Для каждого из задействованных шаблонов существует исходный тип функции и тип преобразованной функции. [ Note: Создание преобразованного типа описано в [temp.func.order]. ] В процессе вывода преобразованный тип используется в качестве шаблона аргумента, а исходный тип другого шаблона - в качестве шаблона параметра. Этот процесс выполняется дважды для каждого типа, участвующего в сравнении частичного упорядочения: один раз с использованием преобразованного шаблона-1 в качестве шаблона аргумента и шаблона-2 в качестве шаблона параметра и снова с использованием преобразованного шаблона-2 в качестве шаблона аргумента и шаблона-1. в качестве шаблона параметра.end note

Типы, используемые для определения порядка, зависят от контекста, в котором выполняется частичное упорядочение:

  • В контексте вызова функции используются те типы параметров функции, для которых у вызова функции есть аргументы.141

  • В контексте вызова функции преобразования используются типы возвращаемых значений шаблонов функций преобразования.

  • В other contexts шаблоне функции используется тип функции.

Каждый тип, указанный выше из шаблона параметра, и соответствующий тип из шаблона аргумента используются как типы P и A. Если конкретный элемент P не содержит тех, template-parameters которые участвуют в выводе аргументов шаблона, это P не используется для определения порядка.

Перед тем, как будет выполнено частичное упорядочение, определенные преобразования выполняются в типах, используемых для частичного упорядочивания:

  • Если P является ссылочным типом, P заменяется указанным типом.

  • Если A является ссылочным типом, A заменяется указанным типом.

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

Удалите все квалификаторы cv верхнего уровня:

  • Если P это тип с квалификацией cv, P он заменяется версией с неквалифицированной версией P.

  • Если A это тип с квалификацией cv, A он заменяется версией с неквалифицированной версией A.

Используя полученные типы P и A, затем выполняется вывод, как описано в [temp.deduct.type]. Если P это параметр функции пакета, тип A каждого оставшегося типа параметра шаблона аргумента сравнивается с типом P из declarator-idпараметра функции пакета. Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции. Точно так же, если он A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра шаблона параметра. Если вывод для данного типа успешен, тип из шаблона аргумента считается как минимум таким же специализированным, как тип из шаблона параметра. [Example:

template<class... Args>           void f(Args... args);         // #1
template<class T1, class... Args> void f(T1 a1, Args... args);  // #2
template<class T1, class T2>      void f(T1 a1, T2 a2);         // #3

f();                // calls #1
f(1, 2, 3);         // calls #2
f(1, 2);            // calls #3; non-variadic template #3 is more specialized
                    // than the variadic templates #1 and #2

end example]

Если для данного типа, удержание преуспевает в обоих направлениях (т.е. типы идентичны после вышеуказанных преобразований) , и оба , P и A были ссылочные типы (прежде , чем быть заменен с типом упомянутого выше):

  • если тип из шаблона аргумента был ссылкой lvalue, а тип из шаблона параметра - нет, тип параметра не считается по крайней мере таким же специализированным, как тип аргумента; иначе,

  • если тип из шаблона аргумента более квалифицирован cv, чем тип из шаблона параметра (как описано выше), тип параметра не считается по крайней мере таким же специализированным, как тип аргумента.

Шаблон функции F является шаблоном at least as specialized asфункции, G если для каждой пары типов, используемых для определения порядка, тип from F является по крайней мере таким же специализированным, как тип from G. F является more specialized than G ли F это , по крайней мере специализированы , как G и G не по крайней мере специализированы , как F.

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

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

template <class T> T f(int);            // #1
template <class T, class U> T f(U);     // #2
void g() {
  f<int>(1);                            // calls #1
}

end example]

[ Note: Частичное упорядочение шаблонов функций, содержащих пакеты параметров шаблона, не зависит от количества выведенных аргументов для этих пакетов параметров шаблона. ] [end noteExample:

template<class ...> struct Tuple { };
template<class ... Types> void g(Tuple<Types ...>);                 // #1
template<class T1, class ... Types> void g(Tuple<T1, Types ...>);   // #2
template<class T1, class ... Types> void g(Tuple<T1, Types& ...>);  // #3

g(Tuple<>());                   // calls #1
g(Tuple<int, float>());         // calls #2
g(Tuple<int, float&>());        // calls #3
g(Tuple<int>());                // calls #3

end example]

Аргументы по умолчанию не считаются аргументами в этом контексте; они становятся аргументами только после выбора функции.

17.8.2.5 Deducing template arguments from a type [temp.deduct.type]

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

В некоторых случаях выведение выполняется с использованием одного набора типов, P а Aв других случаях будет набор соответствующих типов P и A. Вывод типа выполняется независимо для каждой P/A пары, а затем вычисленные значения аргументов шаблона объединяются. Если вывод типа не может быть выполнен для какой-либо P/A пары, или если для любой пары вывод приводит к более чем одному возможному набору выводимых значений, или если разные пары дают разные выведенные значения, или если какой-либо аргумент шаблона остается ни выведенным, ни явно указанным, шаблон вывод аргумента не удается. Тип параметра типа выводится только из привязки массива, если он не выводится иначе.

Данный тип P может состоять из ряда других типов, шаблонов и значений, не являющихся типами:

  • Тип функции включает типы каждого из параметров функции и возвращаемого типа.

  • Указатель на тип члена включает тип указанного объекта класса и тип указанного члена.

  • Тип, который является специализацией шаблона класса (например, A<int>), включает типы, шаблоны и значения, не являющиеся типами, на которые ссылается список аргументов шаблона для данной специализации.

  • Тип массива включает тип элемента массива и значение привязанного массива.

В большинстве случаев типы, шаблоны и значения, не являющиеся типами, которые используются для составления, P участвуют в выводе аргументов шаблона. То есть они могут использоваться для определения значения аргумента шаблона, и определенное таким образом значение должно согласовываться со значениями, определенными в другом месте. Однако в определенных контекстах значение не участвует в выводе типа, а вместо этого использует значения аргументов шаблона, которые были либо выведены в другом месте, либо явно указаны. Если параметр шаблона используется только в невыявленных контекстах и ​​не указан явно, определение аргумента шаблона завершается ошибкой. [ Note: Под [temp.deduct.call] и [temp.deduct.partial], если не P содержит никаких, template-parameters которые появляются в выведенных контекстах, выведение не производится, поэтому P и A не обязательно иметь ту же форму. ]end note

Невыведенные контексты:

  • nested-name-specifier Такого типа , который был определен с помощью qualified-id.

  • Файл expressiona decltype-specifier.

  • Аргумент шаблона, не являющийся типом, или связанный массив, в котором часть выражения ссылается на параметр шаблона.

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

  • Параметр функции, для которого невозможно вывести аргумент, потому что связанный аргумент функции является функцией или набором перегруженных функций ( [over.over]), и применяется одно или несколько из следующего:

    • более одной функции соответствует типу параметра функции (что приводит к неоднозначному выводу), или

    • ни одна функция не соответствует типу параметра функции, или

    • набор функций, предоставленный в качестве аргумента, содержит один или несколько шаблонов функций.

  • Параметр функции, для которого связанный аргумент является списком инициализатора ( [dcl.init.list]), но параметр не имеет типа, для которого задан вывод из списка инициализатора ( [temp.deduct.call]). [Example:

    template<class T> void g(T);
    g({1,2,3});                 // error: no argument deduced for T
    

    end example]

  • Пакет параметров функции, который не встречается в конце parameter-declaration-list.

Когда имя типа указывается способом, который включает невыведенный контекст, все типы, составляющие это имя типа, также не выводятся. Однако составной тип может включать как выведенные, так и невыведенные типы. [ Example: Если тип указан как A<T>​::​B<T2>, оба T и T2 не выводятся. Точно так же, если тип определяется как A<I+J>​::​X<T>, I, J, и T не являются выведены. Если тип указан как void f(typename A<T>​::​B, A<T>), T in A<T>​::​B не выводится, но выводится T in A<T> . ]end example

[ Example: Вот пример, в котором разные пары параметр / аргумент приводят к несогласованным выводам аргументов шаблона:

template<class T> void f(T x, T y) { /* ... */ }
struct A { /* ... */ };
struct B : A { /* ... */ };
void g(A a, B b) {
  f(a,b);           // error: T could be A or B
  f(b,a);           // error: T could be A or B
  f(a,a);           // OK: T is A
  f(b,b);           // OK: T is B
}

Вот пример, в котором два аргумента шаблона выводятся из одной пары параметр / аргумент функции. Это может привести к конфликтам, которые приводят к сбою определения типа:

template <class T, class U> void f(  T (*)( T, U, U )  );

int g1( int, float, float);
char g2( int, float, float);
int g3( int, char, float);

void r() {
  f(g1);            // OK: T is int and U is float
  f(g2);            // error: T could be char or int
  f(g3);            // error: U could be char or float
}

Вот пример, в котором квалификационное преобразование применяется между типом аргумента при вызове функции и выведенным типом аргумента шаблона:

template<class T> void f(const T*) { }
int* p;
void s() {
  f(p);             // f(const int*)
}

Вот пример, в котором аргумент шаблона используется для создания экземпляра типа производного класса соответствующего типа параметра функции:

template <class T> struct B { };
template <class T> struct D : public B<T> {};
struct D2 : public B<int> {};
template <class T> void f(B<T>&){}
void t() {
  D<int> d;
  D2     d2;
  f(d);             // calls f(B<int>&)
  f(d2);            // calls f(B<int>&)
}

end example]

Тип аргумент шаблона T, шаблон аргумент шаблона TT или шаблон без аргументов типа i может быть выведены , если P и A имеет одну из следующих форм:

T
cv-list T
T*
T&
T&&
T[integer-constant]
template-name<T>  (where template-name refers to a class template)
type(T)
T()
T(T)
T type::*
type T::*
T T::*
T (type::*)()
type (T::*)()
type (type::*)(T)
type (T::*)(T)
T (type::*)(T)
T (T::*)()
T (T::*)(T)
type[i]
template-name<i>  (where template-name refers to a class template)
TT<T>
TT<i>
TT<>

где (T) представляет список-типов-параметров ( [dcl.fct]), где по крайней мере один тип-параметра содержит a T, и () представляет список-типов-параметров, где ни один тип параметра не содержит T. Точно так же <T> представляет списки аргументов шаблона, где по крайней мере один аргумент содержит T, <i> представляет списки аргументов шаблона, где по крайней мере один аргумент содержит, i и <> представляет списки аргументов шаблона, где ни один аргумент не содержит T или i.

Если P имеет форму, содержащую <T> или <i>, то каждый аргумент Pi соответствующего списка аргументов шаблона P сравнивается с соответствующим аргументом Ai соответствующего списка аргументов шаблона A. Если список аргументов шаблона P содержит расширение пакета, которое не является последним аргументом шаблона, весь список аргументов шаблона является невыведенным контекстом. Если Pi это расширение пакета, то шаблон Pi сравнивается с каждым оставшимся аргументом в списке аргументов шаблона A. Каждое сравнение выводит аргументы шаблона для последующих позиций в расширенных пакетах параметров шаблона Pi. Во время partial ordering, если Ai изначально было расширение пакета:

  • if P не содержит аргумент шаблона, соответствующий Ai then Ai , игнорируется;

  • в противном случае, если Pi это не расширение пакета, вывести аргумент шаблона не удастся.

[Example:

template<class T1, class... Z> class S;                               // #1
template<class T1, class... Z> class S<T1, const Z&...> { };          // #2
template<class T1, class T2>   class S<T1, const T2&> { };            // #3
S<int, const int&> s;         // both #2 and #3 match; #3 is more specialized

template<class T, class... U>            struct A { };                // #1
template<class T1, class T2, class... U> struct A<T1, T2*, U...> { }; // #2
template<class T1, class T2>             struct A<T1, T2> { };        // #3
template struct A<int, int*>; // selects #2

end example]

Точно так же, если P имеет форму, содержащую (T), то каждый тип параметра Pi соответствующего списка-типа-параметра ( [dcl.fct]) P сравнивается с соответствующим типом Ai параметра соответствующего списка-типов-параметров из A. Если P и A являются типами функций, которые возникли в результате вывода при получении адреса шаблона функции ( [temp.deduct.funcaddr]) или при выводе аргументов шаблона из объявления функции ( [temp.deduct.decl]) Pi и Ai являются параметрами списка типов-параметров верхнего уровня для P и A, соответственно, Pi является настраивается, если это ссылка пересылки ( [temp.deduct.call]) и Ai ссылка lvalue, и в этом случае тип Pi изменяется на тип параметра шаблона (т. е. T&& изменяется на простой T). [ Note: В результате, когда Pi есть T&& и Ai есть X&, скорректированный Pi будет T, в результате чего T будет выведено как X&. ] [ end noteExample:

template <class T> void f(T&&);
template <> void f(int&) { }    // #1
template <> void f(int&&) { }   // #2
void g(int i) {
  f(i);                         // calls f<int&>(int&), i.e., #1
  f(0);                         // calls f<int>(int&&), i.e., #2
}

end example]

Если parameter-declaration соответствует Pi пакету параметров функции, то его тип declarator-idсравнивается с каждым оставшимся типом параметра в списке типов-параметров для A. Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции. Во время partial ordering, если Ai изначально был пакет параметров функции:

  • if P не содержит тип параметра функции, соответствующий Ai then Ai , игнорируется;

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

[Example:

template<class T, class... U> void f(T*, U...) { }  // #1
template<class T>             void f(T) { }         // #2
template void f(int*);                              // selects #1

end example]

Эти формы можно использовать так же, как T и для дальнейшего составления типов. [Example:

X<int> (*)(char[6])

имеет форму

template-name<T> (*)(type[i])

который является вариантом

type (*)(T)

где тип есть X<int> и T есть char[6]. ]end example

Аргументы шаблона не могут быть выведены из аргументов функции, включающих конструкции, отличные от указанных выше.

Когда значение аргумента, соответствующего параметру шаблона, не являющемуся типом, P который объявлен с зависимым типом, выводится из выражения, параметры шаблона в типе P выводятся из типа значения. [Example:

template<long n> struct A { };

template<typename T> struct C;
template<typename T, T n> struct C<A<n>> {
  using Q = T;
};

using R = long;
using R = C<A<2>>::Q;           // OK; T was deduced to long from the
                                // template argument value in the type A<2>

end example] Тип N в типе T[N] это std​::​size_­t. [Example:

template<typename T> struct S;
template<typename T, T n> struct S<int[n]> {
  using Q = T;
};

using V = decltype(sizeof 0);
using V = S<int[42]>::Q;        // OK; T was deduced to std​::​size_­t from the type int[42]

end example]

[Example:

template<class T, T i> void f(int (&a)[i]);
int v[10];
void g() {
  f(v);                         // OK: T is std​::​size_­t
}

end example]

[ Note: За исключением типов ссылок и указателей, основная граница массива не является частью типа параметра функции и не может быть выведена из аргумента:

template<int i> void f1(int a[10][i]);
template<int i> void f2(int a[i][20]);
template<int i> void f3(int (&a)[i][20]);

void g() {
  int v[10][20];
  f1(v);                        // OK: i deduced to be 20
  f1<20>(v);                    // OK
  f2(v);                        // error: cannot deduce template-argument i
  f2<10>(v);                    // OK
  f3(v);                        // OK: i deduced to be 10
}

end note]

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

template <int i> class A { /* ... */ };
template <int i> void g(A<i+1>);
template <int i> void f(A<i>, A<i+1>);
void k() {
  A<1> a1;
  A<2> a2;
  g(a1);                        // error: deduction fails for expression i+1
  g<0>(a1);                     // OK
  f(a1, a2);                    // OK
}

end example] ]end note

[ Note: Параметры шаблона не участвуют в выводе аргументов шаблона, если они используются только в невыведенных контекстах. Например,

template<int i, typename T>
T deduce(typename A<T>::X x,    // T is not deduced here
         T t,                   // but T is deduced here
         typename B<i>::Y y);   // i is not deduced here
A<int> a;
B<77>  b;

int    x = deduce<77>(a.xm, 62, b.ym);
// T is deduced to be int, a.xm must be convertible to A<int>​::​X
// i is explicitly specified to be 77, b.ym must be convertible to B<77>​::​Y

end note]

Если P есть форма, содержащая <i>, и если тип i отличается от типа соответствующего параметра шаблона шаблона, названного заключительным simple-template-id, выведение не выполняется. Если P имеет форму, содержащую [i], и если тип i не является целочисленным типом, дедукция не выполняется. 142 [Example:

template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
  A<1> a;
  f(a);             // error: deduction fails for conversion from int to short
  f<1>(a);          // OK
}

template<const short cs> class B { };
template<short s> void g(B<s>);
void k2() {
  B<1> b;
  g(b);             // OK: cv-qualifiers are ignored on template parameter types
}

end example]

A template-argument может быть выведено из функции, указателя на функцию или указателя на тип функции-члена.

[Example:

template<class T> void f(void(*)(T,int));
template<class T> void foo(T,int);
void g(int,int);
void g(char,int);

void h(int,int,int);
void h(char,int);
int m() {
  f(&g);            // error: ambiguous
  f(&h);            // OK: void h(char,int) is a unique match
  f(&foo);          // error: type deduction fails because foo is a template
}

end example]

Шаблон type-parameter не может быть выведен из типа аргумента функции по умолчанию. [Example:

template <class T> void f(T = 5, T = 7);
void g() {
  f(1);             // OK: call f<int>(1,7)
  f();              // error: cannot deduce T
  f<int>();         // OK: call f<int>(5,7)
}

end example]

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

template <template <class T> class X> struct A { };
template <template <class T> class X> void f(A<X>) { }
template<class T> struct B { };
A<B> ab;
f(ab);              // calls f(A<B>)

end example]

[ Note: Вывод аргументов шаблона с использованием пакетов параметров ( [temp.variadic]) может выводить ноль или более аргументов для каждого пакета параметров. ] [end noteExample:

template<class> struct X { };
template<class R, class ... ArgTypes> struct X<R(int, ArgTypes ...)> { };
template<class ... Types> struct Y { };
template<class T, class ... Types> struct Y<T, Types& ...> { };

template<class ... Types> int f(void (*)(Types ...));
void g(int, float);

X<int> x1;                      // uses primary template
X<int(int, float, double)> x2;  // uses partial specialization; ArgTypes contains float, double
X<int(float, int)> x3;          // uses primary template
Y<> y1;                         // use primary template; Types is empty
Y<int&, float&, double&> y2;    // uses partial specialization; T is int&, Types contains float, double
Y<int, float, double> y3;       // uses primary template; Types contains int, float, double
int fv = f(g);                  // OK; Types contains int, float

end example]

Хотя template-argument соответствующий template-parameter тип bool может быть выведен из привязки массива, результирующее значение всегда будет, true потому что граница массива будет отличной от нуля.

17.8.2.6 Deducing template arguments from a function declaration [temp.deduct.decl]

В объявлении, которое declarator-idссылается на специализацию шаблона функции, выполняется вывод аргументов шаблона, чтобы идентифицировать специализацию, к которой относится объявление. В частности, это делается для explicit instantiations, explicit specializationsи некоторые friend declarations. Это также делается, чтобы определить, соответствует ли специализация шаблона функции освобождения месту размещения operator new ( [basic.stc.dynamic.deallocation], [expr.new]). Во всех этих случаях P это тип шаблона функции, который рассматривается как потенциальное соответствие, и A это либо тип функции из объявления, либо тип функции освобождения, которая будет соответствовать размещению, operator new как описано в [expr.new]. Удержание производится, как описано в [temp.deduct.type].

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

17.8.3 Overload resolution [temp.over]

Шаблон функции может быть перегружен либо (не шаблонными) функциями его имени, либо (другими) шаблонами функций с тем же именем. Когда вызов этого имени записан (явно или неявно с использованием обозначения оператора), template argument deduction и проверка любого явного template arguments значения выполняется для каждого шаблона функции, чтобы найти значения аргументов шаблона (если есть), которые можно использовать с этим шаблоном функции для создания экземпляра специализация шаблона функции, которая может быть вызвана с аргументами вызова. Для каждого шаблона функции, если вывод аргументов и проверка завершаются успешно, template-arguments (выведенные и / или явные) используются для синтеза объявления специализации шаблона одной функции, которая добавляется к набору функций-кандидатов, которые будут использоваться при разрешении перегрузки. Если для данного шаблона функции выведение аргументов не удается или специализация шаблона синтезированной функции была бы неправильно сформирована, такая функция не добавляется к набору функций-кандидатов для этого шаблона. Полный набор функций-кандидатов включает все синтезированные объявления и все одноименные перегруженные функции, не являющиеся шаблонными. Синтезированные объявления обрабатываются как любые другие функции в оставшейся части разрешения перегрузки, за исключением случаев, явно указанных в [over.match.best].143

[Example:

template<class T> T max(T a, T b) { return a>b?a:b; }

void f(int a, int b, char c, char d) {
  int m1 = max(a,b);            // max(int a, int b)
  char m2 = max(c,d);           // max(char a, char b)
  int m3 = max(a,c);            // error: cannot generate max(int,char)
}

Добавление нешаблонной функции

int max(int,int);

в приведенном выше примере разрешит третий вызов, предоставив функцию, которая может быть вызвана max(a,c) после использования стандартного преобразования char в int for c. ]end example

[ Example: Вот пример преобразования аргумента функции, участвующего в template-argument дедукции:

template<class T> struct B { /* ... */ };
template<class T> struct D : public B<T> { /* ... */ };
template<class T> void f(B<T>&);

void g(B<int>& bi, D<int>& di) {
  f(bi);            // f(bi)
  f(di);            // f((B<int>&)di)
}

end example]

[ Example: Вот пример преобразования аргумента функции, не участвующего в template-parameter дедукции:

template<class T> void f(T*,int);       // #1
template<class T> void f(T,char);       // #2

void h(int* pi, int i, char c) {
  f(pi,i);          // #1: f<int>(pi,i)
  f(pi,c);          // #2: f<int*>(pi,c)

  f(i,c);           // #2: f<int>(i,c);
  f(i,i);           // #2: f<int>(i,char(i))
}

end example]

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

template<class T> void f(T);    // declaration

void g() {
  f("Annemarie");               // call of f<const char*>
}

Вызов f правильно сформирован, даже если шаблон f только объявлен и не определен в точке вызова. Программа будет плохо сформирована, если f<const char*>в некоторой единице трансляции не будет присутствовать явно или неявно сгенерированная специализация for . ] end example

Параметры специализаций шаблонов функций не содержат типов параметров шаблона. Набор преобразований, разрешенных для выведенных аргументов, ограничен, потому что процесс вывода аргументов создает шаблоны функций с параметрами, которые либо точно соответствуют аргументам вызова, либо отличаются только способами, которые могут быть объединены разрешенными ограниченными преобразованиями. Невыведенные аргументы допускают полный диапазон преобразований. Также обратите внимание, что это [over.match.best] указывает, что функции, не являющейся шаблоном, будет отдано предпочтение по сравнению со специализацией шаблона, если в остальном эти две функции являются одинаково хорошими кандидатами на совпадение по перегрузке.

17.9 Deduction guides [temp.deduct.guide]

Направляющие выведения используются, когда a template-nameпоявляется как спецификатор типа для deduced class type. Руководства по вычету не могут быть найдены поиском по имени. Вместо этого при выполнении шаблона argument deductionкласса учитываются любые руководства по выводам, объявленные для шаблона класса.

deduction-guide:
	explicitopt template-name ( parameter-declaration-clause ) -> simple-template-id ;

[Example:

template<class T, class D = int>
struct S {
  T data;
};
template<class U>
S(U) -> S<typename U::type>;

struct A {
  using type = short;
  operator type();
};
S x{A()};           // x is of type S<short, int>

end example]

Те же ограничения применяются к руководству parameter-declaration-clause по дедукции, что и к объявлению функции ( [dcl.fct]). simple-template-id Должен назвать шаблон класса специализации. template-name Должна быть такой же , identifier как template-name из simple-template-id. A deduction-guide должен быть объявлен в той же области, что и соответствующий шаблон класса, и для шаблона класса-члена с тем же доступом. Два объявления руководства по выводу в одной и той же единице перевода для одного и того же шаблона класса не должны иметь эквивалента parameter-declaration-clauses.