首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Template argument deduction

为了实例化功能模板,必须知道每个模板参数,但不是每个模板参数都必须指定。如果可能,编译器将从函数参数中推断缺少的模板参数。当尝试函数调用、获取函数模板的地址时,以及在其他情况*

二次

代码语言:javascript
复制
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)
}

二次

这种机制使使用模板运算符成为可能,因为除了将运算符重写为函数调用表达式之外,没有语法可以为操作符指定模板参数:

二次

代码语言:javascript
复制
#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>>
}

二次

模板参数演绎发生在函数模板之后。名称查找%28可能涉及参数相关查找%29及之前模板参数替换%28可能涉及SFINAE29%和过载分辨率...

Template argument deduction is also performed when the name of a class template is used as the type of an object being constructed: 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(& {...})); auto lck = std::lock_guard(foo.mtx); std::lock_guard lck2(foo.mtx, ul); Template argument deduction for class templates takes place in declarations and in explicit cast expressions; see class template deduction for details.

(since C++17)

从函数调用中扣除

模板参数演绎尝试为类型模板参数确定模板参数%28类型T一、模板参数的模板TT和非类型模板参数的值。II%29,可以替换为每个参数P生成类型推演A,它与参数的类型相同。A,调整后列出如下。

如果有多个参数,则每个参数P/A分别推导出对,然后将推导出的模板参数组合起来。如果扣减失败或对任何P/A如果不同的对产生不同的模板参数,或者如果任何模板参数仍然没有被推导或显式指定,则编译失败。

如果从P施予std::initializer_list<P'>A是带括号的列表,然后对初始化程序列表的每个元素执行扣减,包括P'作为参数和列表元素A'作为论点:

二次

代码语言:javascript
复制
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

二次

If removing references and cv-qualifiers from P gives P'N, and A is a non-empty braced-init-list, then deduction is performed as above, except if N is a non-type template parameter, it is deduced from the length of the initializer list: 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); 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

(since C++17)

如果参数包最后一次出现P,然后类型P与类型匹配。A调用的每一个剩余参数。每个匹配项都将扣除Pack展开中下一个位置的模板参数:

二次

代码语言:javascript
复制
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>
}

二次

如果P是函数类型、指向函数类型的指针或指向成员函数类型的指针,如果A是一组重载函数由于不包含函数模板,每次重载时都尝试模板参数推断。如果只有一个成功,则使用成功的演绎。如果没有一个或多个成功,模板参数是非推导上下文%28,参见%29下面:

二次

代码语言:javascript
复制
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

二次

在开始扣减之前,对PA已制作:

1%29P不是引用类型,

A%29如果A是数组类型,A由从数组到指针转换获得的指针类型替换;

B%29,否则,如果A是函数类型,A由从函数到指针转换得到的指针类型替换;

C%29否则,如果A是cv限定类型,则忽略顶级cv限定符以进行扣减:

二次

代码语言:javascript
复制
template<class T> void f(T);
int a[3];
f(a); // P = T, A = int[3], adjusted to int*: deduced T = int*
const int b = 13;
f(b); // P = T, A = const int, adjusted to int: deduced T = int
void g(int);
f(g); // P = T, A = void(int), adjusted to void(*)(int): deduced T = void(*)(int)

二次

2%29P为cv限定类型,则忽略顶级cv限定符以进行扣减.

3%29P是引用类型,P用于扣除。

4%29P是对cv非限定模板参数%28的rvalue引用,因此称为转发参考%29,相应的函数调用参数是lvalue,类型lvalue引用A用于代替A扣减%28注:这是std::forward注:在类模板演绎,类模板的模板参数永远不是转发引用%28,因为C++17%29%29:

二次

代码语言:javascript
复制
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
}

二次

在这些转换之后,扣减过程如下所述:%28cf。部分“从类型中扣除”%29,并试图找到这样的模板参数,从而使推导出的A28%,即,P在上面列出的调整之后,推导出的模板参数%29的替换与转化A,那就是A在上述调整之后。

如果通常从PA如果失败,将额外考虑下列备选方案:

1%29P是一个参考类型,推导出A%28I。e.引用%29所引用的类型可以比转换后的类型更具cv限定性。A*

二次

代码语言:javascript
复制
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 A

二次

2%29转化A可以是另一个指针或指向成员类型的指针,这些指针可以转换为推导的A通过资格转换或函数指针转换%28,因为C++17%29:

二次

代码语言:javascript
复制
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*)

二次

3%29P是一门课P有表格简单模板id,然后转变A可以是推导出的一个派生类。A同样,如果P是指向窗体类的指针。简单模板id,转化A可以是指向由推导出的派生类所指向的派生类的指针。A*

二次

代码语言:javascript
复制
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不要参与模板参数的演绎,而是使用在其他地方推导或显式指定的模板参数。如果模板参数仅用于非推导上下文,且未显式指定,则模板参数演绎失败。

1%29嵌套名称说明符%28所有位于范围解析运算符左侧。::指定的类型的%29合格身份证*

二次

代码语言:javascript
复制
// the identity template, often used to exclude specific arguments from deduction
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) The expression of a decltype-specifier: 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

(since C++14)

3%29非类型模板参数或数组绑定,其中子表达式引用模板。

参数:

二次

代码语言:javascript
复制
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 N

二次

4%29在函数参数的参数类型中使用的模板参数,该参数具有一个默认参数,该参数用于正在对其进行参数推导的调用中:

二次

代码语言:javascript
复制
template<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)

二次

5%29参数P,他的A是一个函数或一组重载,以便多个函数匹配。P或者没有匹配的函数P或者,重载集包括一个或多个函数模板:

二次

代码语言:javascript
复制
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 context

二次

6%29参数P,他的A是一个大括号的列表,但是P不是std::初始化器[医]列出或提及一项:

二次

代码语言:javascript
复制
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

二次

7%29参数P它是一个参数包,不会出现在参数列表的末尾:

二次

代码语言:javascript
复制
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 context

二次

8%29出现在参数中的模板参数列表P,它包括一个不在模板参数列表末尾的Pack展开:

二次

代码语言:javascript
复制
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

二次

9%29P对于数组类型%28,但不引用数组或指向数组%29的指针,主要数组绑定:

二次

代码语言:javascript
复制
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%28嵌套的名称说明符%29,并且T2因为它是同一类型名称的一部分,但在void(*f)(typename A<T>::B, A<T>)TA<T>::B因为有相同的规则%29,因此没有推导%28,而TA<T>是推导出来的。

从类型中扣除

给定函数参数P这取决于一个或多个类型的模板参数。TI,模板模板参数TTi,或非类型模板参数。I和相应的论点A,则在下列情况下进行扣减:P有下列形式之一:

  • T
  • cv-list T
  • T*
  • T&
  • T&&
  • T[integer-constant]
  • class-template-name<T>
  • 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]
  • class-template-name<I>
  • TT<T>
  • TT<I>
  • TT<>

在哪里。

  • (T)是至少一个参数类型包含T的函数参数类型列表;
  • ()是一个函数参数类型列表,其中没有参数包含T;
  • <T>是至少一个参数包含T的模板参数列表;
  • <I>是至少一个参数包含i的模板参数列表;
  • <>是一个模板参数列表,其中没有参数包含T或I。

如果P具有包含模板参数列表的窗体之一。<T><I>,然后每个元素P该模板参数列表的i与相应的模板参数匹配。A我的A...如果最后一次P我是一个包扩展,然后将其模式与模板参数列表中的每个剩余参数进行比较。A.不推导的尾随参数包被推导为空参数包。

如果P具有包含函数参数列表的窗体之一。(T),则每个参数P从该列表中我将与相应的参数进行比较。A我来自A%27s函数参数列表。如果最后一个P我是一个包扩展,然后将其声明器与其余的每个包进行比较。A参数类型列表中的I。A...

表单可以递归地嵌套和处理:X<int>(*)(char[6])type(*)(T),在哪里类型class-template-name<T>而T是type[i]...

Template type argument cannot be deduced from the type of a non-type template argument: template<typename T, T i> void f(double a10); double v10; f(v); // P = double10, A = double10: // i can be deduced to equal 20 // but T cannot be deduced from the type of i

(until C++17)

When the value of the argument corresponding to a non-type template parameter P that is declared with a dependent type is deduced from an expression, the template parameters in the type of P are deduced from the type of the value. 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> The type of N in the type TN is std::size_t. template<class T, T i> void f(int (&a)i); int v10; f(v); // OK: T is std::size_t

(since C++17)

如果在参数列表中使用非类型模板参数,并推导相应的模板参数,则在其封装模板参数列表中指定的模板参数%28的类型,意味着保持引用%29必须与非类型模板参数的类型完全匹配。除了cv-限定符被删除,以及模板参数是从数组绑定中推导出来的情况之外--在这种情况下,允许任何整型,即使是bool--尽管它始终是真的:

二次

代码语言:javascript
复制
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
}

二次

无法从函数默认参数的类型中推断类型模板参数:

二次

代码语言:javascript
复制
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)
}

二次

模板参数的推导可以使用函数调用中使用的模板专门化中使用的类型:

二次

代码语言:javascript
复制
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>)

二次

其他情况

除了函数调用和运算符表达式之外,在下列情况下还使用模板参数演绎:

自动类型演绎

中使用模板参数演绎。声明变量,在推导自动说明符从变量%27s初始化。

参数P如下所示:T变量的声明类型,该变量包括auto,每一次auto被替换为虚设类型模板参数。U或者,如果初始化程序是一个大括号-init-列表,则使用std::initializer_list<U>.争论A初始化表达式。扣除后UPA遵循上述规则,推导出U被替换成T要获得实际的变量类型:

二次

代码语言:javascript
复制
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>

二次

In direct-list-initialization (but not in copy-list-initalization), when deducing the meaning of the auto from a braced-init-list, the braced-init-list must contain only one element, and the type of auto will be the type of that element: 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 C++17 it was std::initializer_list<int>)

(since C++17)

auto-returning functions Template argument deduction is used in declarations of functions, when deducing the meaning of the auto specifier in the function's return type, from the return statement. For auto-returning functions, the parameter P is obtained as follows: in T, the declared return type of the function that includes auto, every occurrence of auto is replaced with an imaginary type template parameter U. The argument A is the expression of the return statement, and if the return statement has no operand, A is void(). After deduction of U from P and A following the rules described above, the deduced U is substituted into T to get the actual return type: auto f() { return 42; } // P = auto, A = 42: // deduced U = int, the return type of f is int If such function has multiple return statements, the deduction is performed for each return statement. All the resulting types must be the same and become the actual return type. If such function has no return statement, A is void() when deducing. Note: the meaning of decltype(auto) placeholder in variable and function declarations does not use template argument deduction.

(since C++14)

过载分辨率

期间使用模板参数演绎。过载分辨率,当从候选模板函数生成专门化时。PA与常规函数调用相同:

二次

代码语言:javascript
复制
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

二次

如果扣减失败,或者如果扣减成功,但是它产生的专门化将是无效的%28--例如,重载操作符,其参数既不是类也不是枚举类型%29,%28,因为C++14%29的专门化不包括在重载集中,类似于SFINAE...

重载集的地址

模板参数的演绎在接受重载集的地址,它包括函数模板。

函数模板的函数类型是P...目标类型的类型A*

二次

代码语言:javascript
复制
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

二次

在这种情况下,还将一个附加规则应用于演绎:当比较函数参数时P我和A我,如果有的话Pi是对cv-不合格模板参数%28a“转发引用”%29的rvalue引用和相应的A那么,我是一个无价值的参考。P我被调整为模板参数类型%28t&&成为T%29。

If the return type of the function template is a placeholder (auto or decltype(auto)), that return type is a non-deduced context and is determined from the instantiation.

(since C++14)

偏序

期间使用模板参数演绎。重载函数模板的偏序...

转换函数模板

选择时使用模板参数推断。用户定义的转换函数模板参数

A转换结果所需的类型。P转换函数模板的返回类型。

如果返回类型是引用类型,则为%29P是所指的类型;

B%29如果返回类型是数组类型,并且A则不是引用类型。P数组到指针转换获得的指针类型;

C%29如果返回类型是函数类型,并且A则不是引用类型。P函数到指针转换得到的函数指针类型;

d%29P是cv限定的,则忽略顶级cv限定符.

如果A是cv限定的,则忽略顶级cv限定符.。如果A是引用类型,引用类型由演绎使用。

如果通常从PA%28如上文所述%29失败,还将考虑下列备选方案:

A%29如果A是一个参考类型,A可能比推导的更符合cv条件。A

B%29A是指向成员类型的指针或指针。A被允许为任何可转换为A按资格转换:

二次

代码语言:javascript
复制
struct A { template<class T> operator T***(); };
A a;
const int* const* const* p1 = a; // 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*)

二次

c) if A is a function pointer type, the deduced A is allowed to be pointer to noexcept function, convertible to A by function pointer conversion; d) if A is a pointer to member function, the deduced A is allowed to be a pointer to noexcept member function, convertible to A by function pointer conversion.

(since C++17)

见成员模板有关转换函数模板的其他规则。

显式实例化

中使用模板参数演绎。显式实例化,,,显式专业化,而那些朋友声明如果声明符-id碰巧引用函数模板%28的专门化,例如,friend ostream& operator<< <> (...)%29,如果没有显式指定或默认所有模板参数,则使用模板参数扣减来确定引用哪个模板%27s专门化。

P被视为潜在匹配的函数模板的类型,以及A声明中的函数类型。如果在偏序%29之后没有匹配或多个匹配%28,则函数声明格式不正确:

二次

代码语言:javascript
复制
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

二次

在这种情况下,还将一个附加规则应用于演绎:当比较函数参数时P我和A我,如果有的话Pi是对cv-不合格模板参数%28a“转发引用”%29的rvalue引用和相应的A那么,我是一个无价值的参考。P我被调整为模板参数类型%28t&&成为T%29。

去分配函数模板

当确定解分配函数模板专门化匹配operator new...

P被视为潜在匹配的函数模板的类型,以及A是与正在考虑的新的布局运算符匹配的去分配函数的函数类型。如果在重载解析%29之后没有匹配或多个匹配%28,则可能会发生%29%的内存泄漏:

二次

代码语言:javascript
复制
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&) { }
}

二次

别名模板

别名模板从未推断:

二次

代码语言:javascript
复制
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

二次

隐式转换

类型扣减不考虑隐式转换%28除了在%29上列出的类型调整:%27s为过载分辨率以后再发生。

However, if deduction succeeds for all parameters that participate in template argument deduction, and all template arguments that aren't deduced are explicitly specified or defaulted, then the remaining function parameters are compared with the corresponding function arguments. For each remaining parameter P with a type that was non-dependent before substitution of any explicitly-specified template arguments, if the corresponding argument A cannot be implicitly converted to P, deduction fails. Parameters with dependent types in which no template-parameters participate in template argument deduction, and parameters that became non-dependent due to substitution of explicitly-specified template arguments will be checked during overload resolution: 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) }

(since C++14)

缺陷报告

以下行为更改缺陷报告追溯应用于先前发布的C++标准。

DR

Applied to

Behavior as published

Correct behavior

CWG 1391

C++14

effect of implicit conversions of the arguments that aren't involved in deduction were not specified

specified as described above

CWG 2052

C++14

deducing an operator with non-class arguments was a hard error (in some compilers)

soft error if there are other overloads

CWG 2091

C++98

deducing a reference non-type parameter did not work due to type mismatch against the argument

type mismatch avoided

代码语言:txt
复制
 ? cppreference.com

在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com