17 Templates [temp]

17.8 Function template specializations [temp.fct.spec]

17.8.2 Template argument deduction [temp.deduct]

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 изначально было расширение пакета:

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

  • в противном случае, если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 изначально был пакет параметров функции:

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

  • в противном случае, если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 потому что граница массива будет отличной от нуля.