17 Templates [temp]

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]