17 Templates [temp]

17.8 Function template specializations [temp.fct.spec]

17.8.2 Template argument deduction [temp.deduct]

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]