17 Templates [temp]

17.8 Function template specializations [temp.fct.spec]

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]), вывод не выполняется и, в случаях объявления, программа имеет неправильный формат.