For faster navigation, this Iframe is preloading the Wikiwand page for 模板参数推导.

模板参数推导

模板参数推导(template argument deduction),是在调用C++模板函数时,由编译器根据使用上下文来推断所调用的模板函数的模板参数。[参1]这一概念也适用于类的模板成员函数。

类模板也存在模板参数推导的情形。例如:

template <class T> struct eval;
template <template <class, class...> class TT, class T1, class... Rest>
     struct eval<TT<T1, Rest...>> { };
eval<A<int>> eA; // OK: matches partial specialization of eval

概念

模板函数在定义时,用template<>声明模板参数(或称模板形参)。调用模板函数时,可以在函数名字后用< >显式指出模板参数(这时称作模板实参)。例如:

 template<class T> void foo(T v1){};
 foo<double>(3);

但是,调用模板函数时,如果不显式指明模板参数,而是根据函数的调用实参去推断模板实参,这就是模板参数推导。例如上例可进一步考虑情形:

 foo(3.14);

编译器会推导出这种函数调用的模板实参为T->double.

推导类型

编译器比较函数模板的形参(template parameter)与对应的调用实参(argument used in the function call)的类型,以确定模板参数的类型。形参的类型必须是下述特定情形之一:[参1]

T
const T
volatile T
T&
T*
T[10]
A<T>
C(*)(T)
T(*)()
T(*)(U)
T C::*
C T::*
T U::*
T (C::*)()
C (T::*)()
D (C::*)(T)
C (T::*)(U)
T (C::*)(U)
T (U::*)()
T (U::*)(V)
E[10][i]
B<i>
TT<T>
TT<i>
TT<C>

说明:

  • T, U, V表示模板类型参数
  • 10表示任意整数常量
  • i表示模板非类型参数
  • [i]表示数组界( represents an array bound of a reference or pointer type, or a non-major array bound of a normal array)
  • TT表示模板的模板参数(template template argument)[参2]
  • (T), (U), (V)表示参数列表包含至少一个模板类型参数
  • ()表示参数列表不包含模板参数
  • <T>表示模板参数列表包含至少一个模板类型参数
  • <i>表示模板参数列表包含至少一个模板非类型参数
  • <C>表示模板参数列表其模板参数不依赖于模板实参

编译器可以从上述几个类型结构的复合类型推导模板参数。

推导规则

对于形如:

template<typename T> int foo(ParamType param);

其模板参数T的类型需要从模板函数的实参与形参依照如下规则推导:

  • 首先,如果模板函数的实参表达式是引用,首先去除引用;
  • 上一步后,如果剩下的实参表达式有顶层的const且/或volatile限定符,去除掉。

因而,从模板函数的实参表达式,不能自动推导出顶层的CV-qualifiers,也不能自动推导出引用类型,需要显式指定。

例如:

   template<class T> void foo(T arg){ arg=101;} // 函数模板     
   const int i=102;
   foo(i); //函数模板实例化为 void foo<int>(int),实参的const限定已被脱去
   foo<const int&>(v1);//直接显示指明模板参数类型
   template<class T> void foo(const T& arg);//或者偏特化模板函数

如果形参还带上&号,声明为引用类型,则不执行const剥除(const-stripping),例如:

   const int i=102;
   const int &j=i;
   template<class T> void foo(T& arg) { arg=101;}// 函数模板 
   foo(j); // 编译错误: 'arg': you cannot assign to a variable that is const	 

这是因为如果不抑制const剥除,则得到了一个非常量引用型变量,绑定到const变量,这显然是不可接受的。

实参表达式为数组,模板参数推导的类型为指针。这是因为数组名在实参表达式中自动隐式转换为首元素地址的右值。例如:

   int a[9];
   template<class T> void foo(T arg){}; // 函数模板 
   foo(a);// 函数模板实例化为 void foo<int*>(int*)

另外,C++11标准明确规定不能由模板参数推导出对应实参为std::initializer_list的类型。[参3]例如:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

类型完美转发

C++11增加了右值引用这一新的数据类型。如:template<class T> void foo(T&& arg);“T&&”并不意味着形参arg的数据类型一定是右值引用。其数据类型既可能是左值引用,也可能是右值引用。依据“引用塌缩规则”,有:

  • 如果实参表达式是类型A的左值,则模板参数T的类型为左值引用A&,形参arg的类型为左值引用A&;
  • 如果实参表达式是类型A的右值(包括纯右值临终值),则模板参数T的类型为右值引用A&&,形参arg的类型为y右值引用A&&。

如此,就把实参的值分类情形完美地传递到模板函数内部,据此可再完美转发给拷贝语义或移动语义的实现函数。

参考文献

    参:
  1. ^ 1.0 1.1 Template argument deduction (C++ only), in IBM XL C/C++ Reference for Linux
  2. ^ 模板的模板参数,例如:Stack<int,std::vector<int> > vStack; // integer stack that uses a vector as internal container
  3. ^ § 14.8.2.5/5 of the C++11 standard explicitly states that this is a non-deduced context for a template argument: A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type.
{{bottomLinkPreText}} {{bottomLinkText}}
模板参数推导
Listen to this article

This browser is not supported by Wikiwand :(
Wikiwand requires a browser with modern capabilities in order to provide you with the best reading experience.
Please download and use one of the following browsers:

This article was just edited, click to reload
This article has been deleted on Wikipedia (Why?)

Back to homepage

Please click Add in the dialog above
Please click Allow in the top-left corner,
then click Install Now in the dialog
Please click Open in the download dialog,
then click Install
Please click the "Downloads" icon in the Safari toolbar, open the first download in the list,
then click Install
{{::$root.activation.text}}

Install Wikiwand

Install on Chrome Install on Firefox
Don't forget to rate us

Tell your friends about Wikiwand!

Gmail Facebook Twitter Link

Enjoying Wikiwand?

Tell your friends and spread the love:
Share on Gmail Share on Facebook Share on Twitter Share on Buffer

Our magic isn't perfect

You can help our automatic cover photo selection by reporting an unsuitable photo.

This photo is visually disturbing This photo is not a good choice

Thank you for helping!


Your input will affect cover photo selection, along with input from other users.

X

Get ready for Wikiwand 2.0 🎉! the new version arrives on September 1st! Don't want to wait?