Выведение шаблонных аргументов
Для того, чтобы создать экземпляр шаблона функции, каждый шаблонный аргумент должен быть известен, но не каждый аргумент должен быть указан. Когда это возможно, компилятор выведет недостающие шаблонные аргументы из аргументов функции. Это происходит при попытке вызова функции, при взятии адреса шаблона функции и в некоторых других контекстах:
template<typename To, typename From>
To convert(From f);
void g(double d)
{
int i = convert<int>(d); // calls convert<int, double>(double)
char c = convert<char>(d); // calls convert<char, double>(double)
int(*ptr)(float) = convert; // instantiates convert<int, float>(float)
// and stores its address in ptr
}Этот механизм позволяет использовать шаблоновые операторы, так как нет синтаксиса для указания шаблонных аргументов для оператора, кроме как повторного написания его как выражения вызова функции:
#include <iostream>
int main()
{
std::cout << "Hello, world" << std::endl;
// operator<< is looked up via ADL as std::operator<<,
// then deduced to operator<<<char, std::char_traits<char>> both times
// std::endl is deduced to &std::endl<char, std::char_traits<char>>
}Выведение шаблонных аргументов происходит после поиска имени шаблона функции (что может включать поиск аргументов-зависимого поиска) и перед подстановкой шаблонных аргументов (что может включать SFINAE) и разрешением перегрузки.
| Выведение шаблонных аргументов также выполняется, когда имя шаблона класса используется как тип объекта, который создается: std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);
std::copy_n(vi1, 3, std::back_insert_iterator(vi2));
std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...}));
auto lck = std::lock_guard(foo.mtx);
std::lock_guard lck2(foo.mtx, ul);Выведение шаблонных аргументов для шаблонов классов происходит в объявлениях и в явных выражениях приведения типов; см. выведение шаблонных аргументов для шаблонов классов для подробностей. | (с C++17) |
Выведение из вызова функции
Выведение шаблонных аргументов пытается определить шаблонные аргументы (типы для параметров шаблонов типов Ti, шаблоны для параметров шаблонов шаблонов TTi и значения для параметров шаблонов нетипового типа Ii), которые могут быть подставлены в каждый параметр P для получения выведенного A типа, который совпадает с типом аргумента A, после корректировок, перечисленных ниже.
Если есть несколько параметров, каждый P/A набор выводится отдельно, а выведенные шаблонные аргументы затем объединяются. Если выведение терпит неудачу или является неоднозначным для любого P/A набора, или если разные наборы дают разные выведенные шаблоны аргументы, или если любой шаблониый аргумент остается невыведенным и не явно указанным, компиляция терпит неудачу.
| Если удаление ссылок и cv-квалификаторов из template<class T>
void f(std::initializer_list<T>);
f({1, 2, 3}); // P = std::initializer_list<T>, A = {1, 2, 3}
// P'1 = T, A'1 = 1: deduced T = int
// P'2 = T, A'2 = 2: deduced T = int
// P'3 = T, A'3 = 3: deduced T = int
// OK: deduced T = int
f({1, "abc"}); // P = std::initializer_list<T>, A = {1, "abc"}
// P'1 = T, A'1 = 1: deduced T = int
// P'2 = T, A'2 = "abc": deduced T = const char*
// error: deduction fails, T is ambiguousЕсли удаление ссылок и cv-квалификаторов из template<class T, int N>
void h(T const(&)[N]);
h({1, 2, 3}); // deduced T = int, deduced N = 3
template<class T>
void j(T const(&)[3]);
j({42}); // deduced T = int, array bound is not a parameter, 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: deduced N = 3
template<int M, int N>
void m(int const(&)[M][N]);
m({{1, 2}, {3, 4}}); // deduced M = 2, deduced N = 2
template<class T, int N>
void n(T const(&)[N], T);
n({{1}, {2}, {3}}, Aggr()); // deduced T = Aggr, deduced N = 3Если параметр-упаковка появляется в качестве последнего template<class... Types>
void f(Types&...);
void h(int x, float& y)
{
const int z = x;
f(x, y, z); // P = Types&..., A1 = x: deduced first member of Types... = int
// P = Types&..., A2 = y: deduced second member of Types... = float
// P = Types&..., A3 = z: deduced third member of Types... = const int
// calls f<int, float, const int>
} | (с C++11) |
Если P является типом функции, указателем на тип функции или указателем на тип члена-функции и если A является множеством перегруженных функций, не содержащих шаблонов функций, выведение шаблонных аргументов выполняется с каждой перегрузкой. Если только одна удается, то это успешное выведение используется. Если ни одна, или более одной удается, то параметр шаблона находится в контексте, не подлежащем выводу (см. ниже):
template<class T>
int f(T(*p)(T));
int g(int);
int g(char);
f(g); // P = T(*)(T), A = overload set
// P = T(*)(T), A1 = int(int): deduced T = int
// P = T(*)(T), A2 = int(char): fails to deduce T
// only one overload works, deduction succeedsПеред началом выведения выполняются следующие корректировки для P и A.
P не является типом ссылки, A является типом массива, A заменяется типом указателя, полученным из преобразования массив-указатель;A является типом функции, A заменяется типом указателя, полученным из преобразования функция-указатель;A является cv-квалифицированным типом, верхние cv-квалификаторы игнорируются для вывода: template<class T> void f(T); int a[3]; f(a); // P = T, A = int[3], adjusted to int*: deduced T = int* void b(int); f(b); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int) const int c = 13; f(c); // P = T, A = const int, adjusted to int: deduced T = int
P является cv-квалифицированным типом, верхние cv-квалификаторы игнорируются для вывода.P является типом ссылки, тип, на который ссылается, используется для вывода.P является ссылкой-значением на cv-неквалифицированный параметр шаблона (так называемая ссылка-перенаправления), и соответствующий аргумент вызова функции является l-значением, тип l-ссылки на A используется вместо A для вывода (Примечание: это основа для действия std::forward Примечание: в выведении шаблонных аргументов для шаблонов классов, параметр шаблона шаблона класса никогда не является ссылкой-перенаправлением(с C++17)): template<class T>
int f(T&&); // P is an rvalue reference to cv-unqualified T (forwarding reference)
template<class T>
int g(const T&&); // P is an rvalue reference to cv-qualified T (not special)
int main()
{
int i;
int n1 = f(i); // argument is lvalue: calls f<int&>(int&) (special case)
int n2 = f(0); // argument is not lvalue: calls f<int>(int&&)
// int n3 = g(i); // error: deduces to g<int>(const int&&), which
// cannot bind an rvalue reference to an lvalue
}После этих преобразований процесс вывода происходит как описано ниже (см. раздел выведение из типа) и пытается найти такие шаблоны аргументов, которые бы сделали выведенный A (то есть P после корректировок, перечисленных выше, и подстановки выведенных шаблонов параметров) идентичным преобразованному A, то есть A после корректировок, перечисленных выше.
Если обычное выведение из P и A терпит неудачу, то дополнительно рассматриваются следующие альтернативы:
P является типом ссылки, выведенный A (то есть тип, на который ссылается ссылка) может быть более cv-квалифицированным, чем преобразованный A: template<typename T>
void f(const T& t);
bool a = false;
f(a); // P = const T&, adjusted to const T, A = bool:
// deduced T = bool, deduced A = const bool
// deduced A is more cv-qualified than AA может быть другим типом указателя или указателя на член, который может быть преобразован к выведенному A через преобразования квалификации или преобразование указателя на функцию(с C++17): template<typename T>
void f(const T*);
int* p;
f(p); // P = const T*, A = int*:
// deduced T = int, deduced A = const int*
// qualification conversion applies (from int* to const int*)P — класс, и P имеет вид простого-идентификатора-шаблона, то преобразованный A может быть производным классом от выведенного A. Аналогично, если P — указатель на класс вида простой-идентификатор-шаблона, то преобразованный A может быть указателем на производный класс, на который указывает выведенный A: template<class T>
struct B {};
template<class T>
struct D : public B<T> {};
template<class T>
void f(B<T>&) {}
void f()
{
D<int> d;
f(d); // P = B<T>&, adjusted to P = B<T> (a simple-template-id), A = D<int>:
// deduced T = int, deduced A = B<int>
// A is derived from deduced A
}Контексты, не подлежащие выводу
В следующих случаях типы, шаблоны и значения нетипового типа, используемые для составления P , не участвуют в выводе шаблонных аргументов, а вместо этого используют шаблоны аргументов, которые были либо выведены в другом месте, либо явно указаны. Если параметр шаблона используется только в контекстах, не подлежащих выводу, и не указан явно, выведение шаблонных аргументов терпит неудачу.
::) типа, который был указан с помощью квалифицированного-идентификатора: // the identity template, often used to exclude specific arguments from deduction
// (available as std::type_identity as of C++20)
template<typename T>
struct identity { typedef T type; };
template<typename T>
void bad(std::vector<T> x, T value = 1);
template<typename T>
void good(std::vector<T> x, typename identity<T>::type value = 1);
std::vector<std::complex<double>> x;
bad(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
// P1/A1: deduced T = std::complex<double>
// P2 = T, A2 = double
// P2/A2: deduced T = double
// error: deduction fails, T is ambiguous
good(x, 1.2); // P1 = std::vector<T>, A1 = std::vector<std::complex<double>>
// P1/A1: deduced T = std::complex<double>
// P2 = identity<T>::type, A2 = double
// P2/A2: uses T deduced by P1/A1 because T is to the left of :: in P2
// OK: T = std::complex<double> 2) спецификатор индексирования упаковки или выражение индексирования упаковки: template<typename... Ts> void f(Ts...[0], std::tuple<Ts...>); f(3, std::tuple(5, 'A')); // P2 = std::tuple<Ts...>, A2 = std::tuple<int, char> // P2/A2: deduced first member of Ts... = int // P2/A2: deduced second member of Ts... = char // P1 = Ts...[0], A1 = int: Ts...[0] is in non-deduced context | (с C++26) |
3) Выражение decltype-спецификатора: template<typename T> void f(decltype(*std::declval<T>()) arg); int n; f<int*>(n); // P = decltype(*declval<T>()), A = int: T is in non-deduced context | (с C++11) |
template<std::size_t N>
void f(std::array<int, 2 * N> a);
std::array<int, 10> a;
f(a); // P = std::array<int, 2 * N>, A = std::array<int, 10>:
// 2 * N is non-deduced context, N cannot be deduced
// note: f(std::array<int, N> a) would be able to deduce Ntemplate<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = std::less<T>());
std::vector<std::string> v(3);
f(v); // P1 = const std::vector<T>&, A1 = std::vector<std::string> lvalue
// P1/A1 deduced T = std::string
// P2 = const F&, A2 = std::less<std::string> rvalue
// P2 is non-deduced context for F (template parameter) used in the
// parameter type (const F&) of the function parameter comp,
// that has a default argument that is being used in the call f(v)P, чьё A является функцией или множеством перегрузок таким образом, что более одной функции соответствует P или ни одна функция не соответствует P или множество перегрузок включает одну или более шаблоны функций: template<typename T>
void out(const T& value) { std::cout << value; }
out("123"); // P = const T&, A = const char[4] lvalue: deduced T = char[4]
out(std::endl); // P = const T&, A = function template: T is in non-deduced contextP, чьё A — список инициализации, но P не std::initializer_list, ссылка на один (возможно, с квалификаторами cv)-объект, или ссылка на массив(с C++17): template<class T>
void g1(std::vector<T>);
template<class T>
void g2(std::vector<T>, T x);
g1({1, 2, 3}); // P = std::vector<T>, A = {1, 2, 3}: T is in non-deduced context
// error: T is not explicitly specified or deduced from another P/A
g2({1, 2, 3}, 10); // P1 = std::vector<T>, A1 = {1, 2, 3}: T is in non-deduced context
// P2 = T, A2 = int: deduced T = int 8) Параметр P, который является пакетом параметров и не находится в конце списка параметров: template<class... Ts, class T>
void f1(T n, Ts... args);
template<class... Ts, class T>
void f2(Ts... args, T n);
f1(1, 2, 3, 4); // P1 = T, A1 = 1: deduced T = int
// P2 = Ts..., A2 = 2, A3 = 3, A4 = 4: deduced Ts = [int, int, int]
f2(1, 2, 3, 4); // P1 = Ts...: Ts is non-deduced contextP, и который включает расширение пакета, которое не находится в самом конце списка параметров шаблона: template<int...>
struct T {};
template<int... Ts1, int N, int... Ts2>
void good(const T<N, Ts1...>& arg1, const T<N, Ts2...>&);
template<int... Ts1, int N, int... Ts2>
void bad(const T<Ts1..., N>& arg1, const T<Ts2..., N>&);
T<1, 2> t1;
T<1, -1, 0> t2;
good(t1, t2); // P1 = const T<N, Ts1...>&, A1 = T<1, 2>:
// deduced N = 1, deduced Ts1 = [2]
// P2 = const T<N, Ts2...>&, A2 = T<1, -1, 0>:
// deduced N = 1, deduced Ts2 = [-1, 0]
bad(t1, t2); // P1 = const T<Ts1..., N>&, A1 = T<1, 2>:
// <Ts1..., N> is non-deduced context
// P2 = const T<Ts2..., N>&, A2 = T<1, -1, 0>:
// <Ts2..., N> is non-deduced context | (с C++11) |
P типа массива (но не ссылки на массив или указателя на массив), граничная величина массива: template<int i>
void f1(int a[10][i]);
template<int i>
void f2(int a[i][20]); // P = int[i][20], array type
template<int i>
void f3(int (&a)[i][20]); // P = int(&)[i][20], reference to array
void g()
{
int a[10][20];
f1(a); // OK: deduced i = 20
f1<20>(a); // OK
f2(a); // error: i is non-deduced context
f2<10>(a); // OK
f3(a); // OK: deduced i = 10
f3<10>(a); // OK
}В любом случае, если какая-либо часть имени типа не выводится, всё имя типа является контекстом без вывода. Однако составные типы могут содержать как выводимые, так и не выводимые имена типов. Например, в A<T>::B<T2>, T не выводится из-за правила #1 (квалификатор имени), а T2 не выводится, потому что это часть того же имени типа, но в void(*f)(typename A<T>::B, A<T>), T в A<T>::B не выводится (из-за того же правила), в то время как T в A<T> выводится.
Вывод типа
Для параметра функции P, который зависит от одного или нескольких параметров шаблона типа Ti, параметров шаблона шаблонов TTi или параметров шаблона нетипа Ii, и соответствующего аргумента A, вывод происходит, если P имеет одну из следующих форм:
-
cv(необязательно)T; -
T*; -
T&;
| (с C++11) |
-
T(необязательно)[I(необязательно)];
| (до C++17) |
| (с C++17) |
-
T(необязательно)U(необязательно)::*; -
TT(необязательно)<T>; -
TT(необязательно)<I>; -
TT(необязательно)<TU>; -
TT(необязательно)<>.
В вышеуказанных формах,
-
T(необязательно) илиU(необязательно) представляет собой тип или список_типов_параметров, который либо рекурсивно удовлетворяет этим правилам, является контекстом без вывода вPилиA, или является тем же независимым типом вPиA. -
TT(необязательно) илиTU(необязательно) представляет собой либо шаблон класса, либо параметр шаблона шаблона. -
I(необязательно) представляет собой выражение, которое либо являетсяI, либо зависит от значения вPилиA, или имеет то же постоянное значение вPиA.
| (с C++17) |
Если P имеет одну из форм, включающих список параметров шаблона <T> или <I>, то каждый элемент Pi этого списка аргументов шаблона сопоставляется с соответствующим аргументом шаблона Ai его A. Если последний Pi является расширением пакета, то его шаблон сравнивается с каждым оставшимся аргументом в списке аргументов шаблона A. Прицепившийся пакет параметров, который иначе не выводится, выводится как пустой пакет параметров.
Если P имеет одну из форм, включающих список параметров функции (T), то каждый параметр Pi из этого списка сравнивается с соответствующим аргументом Ai из списка параметров функции A. Если последний Pi является расширением пакета, то его декларатор сравнивается с каждым оставшимся Ai в списке типов параметров A.
Формы могут быть вложенными и обрабатываться рекурсивно:
-
X<int>(*)(char[6])является примеромT*, гдеTявляетсяX<int>(char[6]);
| (до C++17) |
| (с C++17) |
-
X<int>является примеромTT(необязательно)<T>, гдеTTявляетсяXиTявляетсяint, и -
char[6]является примеромT(необязательно)[I(необязательно)], гдеTявляетсяcharиIявляетсяstd::size_t(6).
| Аргумент шаблона типа не может быть выведен из типа аргумента шаблона нетипа: template<typename T, T i>
void f(double a[10][i]);
double v[10][20];
f(v); // P = double[10][i], A = double[10][20]:
// i can be deduced to equal 20
// but T cannot be deduced from the type of i | (до C++17) |
| Когда значение аргумента, соответствующего параметру шаблона нетипа P, объявленному с зависимым типом, выводится из выражения, параметры шаблона в типе P выводятся из типа значения. template<long n>
struct A {};
template<class T>
struct C;
template<class T, T n>
struct C<A<n>> { using Q = T; };
typedef long R;
typedef C<A<2>>::Q R; // OK: T was deduced to long
// from the template argument value in the type A<2>
template<auto X>
class bar {};
template<class T, T n>
void f(bar<n> x);
f(bar<3>{}); // OK: T was deduced to int (and n to 3)
// from the template argument value in the type bar<3>Тип template<class T, T i> void f(int (&a)[i]); int v[10]; f(v); // OK: T is std::size_t Тип template<bool>
struct A {};
template<auto>
struct B;
template<auto X, void (*F)() noexcept(X)>
struct B<F> { A<X> ax; };
void f_nothrow() noexcept;
B<f_nothrow> bn; // OK: X is deduced as true and the type of X is deduced as bool. | (с C++17) |
Если параметр шаблона нетипа функции-шаблона используется в списке параметров шаблона параметра функции (который также является шаблоном), и соответствующий аргумент шаблона выводится, тип выведенного аргумента шаблона (как указано в его окружающем списке параметров шаблона, означая, что ссылки сохраняются) должен точно совпадать с типом параметра шаблона нетипа, за исключением того, что cv-квалификаторы опускаются, и за исключением случаев, когда аргумент шаблона выводится из границы массива — в этом случае разрешается любой целочисленный тип, даже bool, хотя он всегда станет true:
template<int i>
class A {};
template<short s>
void f(A<s>); // the type of the non-type template param is short
void k1()
{
A<1> a; // the type of the non-type template param of a is int
f(a); // P = A<(short)s>, A = A<(int)1>
// error: deduced non-type template argument does not have the same
// type as its corresponding template argument
f<1>(a); // OK: the template argument is not deduced,
// this calls f<(short)1>(A<(short)1>)
}
template<int&>
struct X;
template<int& R>
void k2(X<R>&);
int n;
void g(X<n> &x)
{
k2(x); // P = X<R>, A = X<n>
// parameter type is int&
// argument type is int& in struct X's template declaration
// OK (with CWG 2091): deduces R to refer to n
}Параметр шаблона типа не может быть выведен из типа аргумента по умолчанию функции:
template<typename T>
void f(T = 5, T = 7);
void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}Вывод параметра шаблона шаблона может использовать тип, используемый в специализации шаблона, используемой в вызове функции:
template<template<typename> class X>
struct A {}; // A is a template with a TT param
template<template<typename> class TT>
void f(A<TT>) {}
template<class T>
struct B {};
A<B> ab;
f(ab); // P = A<TT>, A = A<B>: deduced TT = B, calls f(A<B>)Другие контексты
Помимо вызовов функций и операторных выражений, вывод аргументов шаблона используется в следующих ситуациях:
Вывод типа autoВывод шаблона аргумента используется в объявлениях переменных, при выводе значения спецификатора auto из инициализатора переменной. Параметр const auto& x = 1 + 2; // P = const U&, A = 1 + 2:
// same rules as for calling f(1 + 2) where f is
// template<class U> void f(const U& u)
// deduced U = int, the type of x is const int&
auto l = {13}; // P = std::initializer_list<U>, A = {13}:
// deduced U = int, the type of l is std::initializer_list<int>При прямой списковой инициализации (но не при копирующей списковой инициализации), при выводе значения auto из списка инициализации, список инициализации должен содержать только один элемент, и тип auto будет типом этого элемента: auto x1 = {3}; // x1 is std::initializer_list<int>
auto x2{1, 2}; // error: not a single element
auto x3{3}; // x3 is int
// (before N3922 x2 and x3 were both std::initializer_list<int>) | (с C++11) |
Функции, возвращающие autoВывод шаблона аргумента используется в объявлениях функций, при выводе значения спецификатора auto в типе возвращаемого значения функции из оператора возврата. Для функций, возвращающих auto, параметр auto f() { return 42; } // P = auto, A = 42:
// deduced U = int, the return type of f is intЕсли такая функция имеет несколько операторов возврата, вывод выполняется для каждого оператора возврата. Все полученные типы должны быть одинаковыми и становятся фактическим типом возвращаемого значения. Если такая функция не имеет оператора возврата, Примечание: значение заполнителя | (с C++14) |
Разрешение перегрузки
Вывод шаблона аргумента используется при разрешении перегрузки, при генерации специализаций от кандидатной шаблонной функции. P и A такие же, как и при обычном вызове функции:
std::string s; std::getline(std::cin, s); // "std::getline" names 4 function templates, // 2 of which are candidate functions (correct number of parameters) // 1st candidate template: // P1 = std::basic_istream<CharT, Traits>&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // deduction determines the type template parameters CharT, Traits, and Allocator // specialization std::getline<char, std::char_traits<char>, std::allocator<char>> // 2nd candidate template: // P1 = std::basic_istream<CharT, Traits>&&, A1 = std::cin // P2 = std::basic_string<CharT, Traits, Allocator>&, A2 = s // deduction determines the type template parameters CharT, Traits, and Allocator // specialization std::getline<char, std::char_traits<char>, std::allocator<char>> // overload resolution ranks reference binding from lvalue std::cin // and picks the first of the two candidate specializations
Если вывод завершается неудачей или если вывод успешен, но созданная специализация будет неверной (например, перегруженный оператор, параметры которого не являются классами или перечислениями), специализация не включается в набор перегрузок, аналогично SFINAE.
Адрес набора перегрузок
Вывод шаблона аргумента используется при взятии адреса набора перегрузок, который включает шаблоны функций.
Тип функции шаблона функции — P. Целевой тип — тип A:
std::cout << std::endl; // std::endl names a function template // type of endl P = // std::basic_ostream<CharT, Traits>& (std::basic_ostream<CharT, Traits>&) // operator<< parameter A = // std::basic_ostream<char, std::char_traits<char>>& (*)( // std::basic_ostream<char, std::char_traits<char>>& // ) // (other overloads of operator<< are not viable) // deduction determines the type template parameters CharT and Traits
В этом случае применяется дополнительное правило к выводу: при сравнении параметров функций Pi и Ai, если любой Pi является ссылкой на rvalue на cv-неквалифицированный параметр шаблона («передающая ссылка») и соответствующий Ai является ссылкой на lvalue, то Pi корректируется до типа параметра шаблона (T&& становится T).
| Если тип возвращаемого значения шаблона функции является заполнителем ( | (с C++14) |
Частичный порядок
Вывод шаблона аргумента используется при частичном упорядочении перегруженных шаблонов функций.
Шаблон функции преобразования
Вывод шаблона аргумента используется при выборе шаблонов пользовательских функций преобразования.
A — это тип, который требуется в результате преобразования. P — тип возвращаемого значения шаблона функции преобразования. Если P — тип ссылки, то тип, на который ссылается P, используется вместо P для последующих частей раздела.
Если A не является типом ссылки:
P — тип массива, то тип указателя, полученный при преобразовании массива в указатель, используется вместо P;P — тип функции, то тип указателя на функцию, полученный при преобразовании функции в указатель, используется вместо P;P — cv-квалифицированный тип, то верхнеуровневые cv-квалификаторы игнорируются.Если A — cv-квалифицированный тип, то верхнеуровневые cv-квалификаторы игнорируются. Если A — тип ссылки, тип, на который она ссылается, используется при выводе.
Если обычный вывод из P и A (как описано выше) завершается неудачей, рассматриваются следующие альтернативы:
A — тип ссылки, A может быть более cv-квалифицированным, чем выведенный A;A — тип указателя или указателя на член, выведенный A может быть любым указателем, который может быть преобразован в A с помощью преобразования квалификации: struct C
{
template<class T>
operator T***();
};
C c;
const int* const* const* p1 = c;
// P = T***, A = const int* const* const*
// regular function-call deduction for
// template<class T> void f(T*** p) as if called with the argument
// of type const int* const* const* fails
// additional deduction for conversion functions determines T = int
// (deduced A is int***, convertible to const int* const* const*) в) если A — тип указателя на функцию, выведенный A может быть указателем на функцию noexcept, преобразуемый в A с помощью преобразования указателя на функцию; г) если A — тип указателя на член функции, выведенный A может быть указателем на noexcept-функцию члена, преобразуемый в A с помощью преобразования указателя на функцию. | (с C++17) |
См. шаблон члена для других правил, касающихся шаблонов функций преобразования.
Явное инстанцирование
Вывод шаблона аргумента используется в явных инстанцированиях, явных специализациях и тех объявлениях friend, где идентификатор-декларатор ссылается на специализацию шаблона функции (например, friend ostream& operator<< <> (...)), если не все параметры шаблона указаны явно или имеют значения по умолчанию, вывод шаблона аргумента используется для определения специализации шаблона, на которую ссылаются.
P — это тип шаблона функции, который рассматривается как потенциальная соответствие, а A — тип функции из объявления. Если соответствий нет или их больше одного (после частичного упорядочения), объявление функции является некорректным:
template<class X>
void f(X a); // 1st template f
template<class X>
void f(X* a); // 2nd template f
template<>
void f<>(int* a) {} // explicit specialization of f
// P1 = void(X), A1 = void(int*): deduced X = int*, f<int*>(int*)
// P2 = void(X*), A2 = void(int*): deduced X = int, f<int>(int*)
// f<int*>(int*) and f<int>(int*) are then submitted to partial ordering
// which selects f<int>(int*) as the more specialized templateВ этом случае к выводу применяется дополнительное правило: при сравнении параметров функций Pi и Ai, если любой Pi является ссылкой на rvalue на cv-неквалифицированный параметр шаблона («передающая ссылка») и соответствующий Ai является ссылкой на lvalue, то Pi корректируется до типа параметра шаблона (T&& становится T).
Шаблон функции освобождения
Вывод шаблона аргумента используется при определении соответствия специализации шаблона функции освобождения данному размещенному виду operator new.
P — это тип шаблона функции, который рассматривается как потенциальное соответствие, а A — тип функции освобождения, который был бы соответствием для рассматриваемого размещенного оператора new. Если совпадений нет или их больше одного (после разрешения перегрузки), размещенная функция освобождения не вызывается (может произойти утечка памяти):
struct X
{
X() { throw std::runtime_error(""); }
static void* operator new(std::size_t sz, bool b) { return ::operator new(sz); }
static void* operator new(std::size_t sz, double f) { return ::operator new(sz); }
template<typename T>
static void operator delete(void* ptr, T arg)
{
::operator delete(ptr);
}
};
int main()
{
try
{
X* p1 = new (true) X; // when X() throws, operator delete is looked up
// P1 = void(void*, T), A1 = void(void*, bool):
// deduced T = bool
// P2 = void(void*, T), A2 = void(void*, double):
// deduced T = double
// overload resolution picks operator delete<bool>
}
catch(const std::exception&) {}
try
{
X* p1 = new (13.2) X; // same lookup, picks operator delete<double>
}
catch(const std::exception&) {}
}Шаблоны псевдонимов
Шаблоны псевдонимов не выводятся , за исключением вывода аргументов шаблона класса(с C++20):
template<class T>
struct Alloc {};
template<class T>
using Vec = vector<T, Alloc<T>>;
Vec<int> v;
template<template<class, class> class TT>
void g(TT<int, Alloc<int>>);
g(v); // OK: deduced TT = vector
template<template<class> class TT>
void f(TT<int>);
f(v); // error: TT cannot be deduced as "Vec" because Vec is an alias templateНеявные преобразования
Выведение типа не учитывает неявные преобразования (кроме указанных выше корректировок типов): это задача разрешения перегрузки, которая происходит позже. Однако, если выведение успешно для всех параметров, участвующих в выводе шаблонных аргументов, и все шаблоные аргументы, которые не выведены, явно указаны или имеют значения по умолчанию, то оставшиеся параметры функции сравниваются с соответствующими аргументами функции. Для каждого оставшегося параметра P с типом, который был независимым до подстановки любых явно указанных шаблоных аргументов, если соответствующий аргумент A не может быть неявным образом преобразован к P, выведение терпит неудачу.
Параметры с зависимыми типами, в которых ни один шаблоный параметр не участвует в выводе шаблоных аргументов, и параметры, которые стали независимыми из-за подстановки явно указанных шаблоных аргументов, будут проверены во время разрешения перегрузки:
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); // for #1, deduction determines T = struct A, but the remaining argument 1
// cannot be implicitly converted to its parameter void*: deduction fails
// instantiation of the return type is not requested
// for #2, deduction determines T = struct A, and the remaining argument 1
// can be implicitly converted to its parameter int: deduction succeeds
// the function call compiles as a call to #2 (deduction failure is SFINAE)
}Отчеты о дефектах
Следующие отчеты о дефектах, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применимо к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| CWG 70 | C++98 | не было указано, будут ли выводиться границы массива | указано как не выводимое |
| CWG 300 | C++98 | выведение происходило для параметров функций видаtype(*)(T)/T(*)()/T(*)(T), указатели на функциисоответствуют этим формам, но ссылки на функции — нет | изменение этих форм наtype(T)/T()/T(T) , чтобы онитакже могли охватывать ссылки |
| CWG 322 | C++98 | параметры типа ссылки не корректировались для использования типа ссылки при выводе | корректировка добавлена |
| CWG 976 | C++98 | при выводе для шаблонов операторов преобразования,const T& тип возврата никогда не мог совпадать с T типом результата | правила скорректированы, чтобы разрешать такие соответствия |
| CWG 1387 | C++11 | выражение decltype-спецификатора не являлось контекстом, не требующим вывода | является |
| CWG 1391 | C++98 | эффект неявных преобразований аргументов, которые не участвуют в выводе, не был указан | указано как описано выше |
| CWG 1591 | C++11 | нельзя вывести размер массива и тип элемента из списка инициализации | разрешено выведение |
| CWG 2052 | C++98 | вывод оператора с неклассовыми, неперечислимыми аргументами был ошибкой | мягкая ошибка, если есть другие перегрузки |
| CWG 2091 | C++98 | вывод параметра ссылки нетипа не работал из-за несоответствия типов с аргументом | несоответствие типов устранено |
| N3922 | C++11 | прямая инициализация списком auto выводит std::initializer_list | некорректно для более чем одного элемента, вывести тип элемента для одного элемента |
| CWG 2355 | C++17 | значение в noexcept спецификаторе типа функции не поддавалось выводу | сделано выводимым |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/template_argument_deduction