模板编程 学习


模板分为函数模板和类模板

1、函数模板是一个独立于类型的函数,可作为一种方式,产生函数的特定类型版本。

模板定义以关键字 template 开始,后接模板形参表,模板形参表是用尖括号括住的一个或多个模板形参的列表,形参之间以逗号分隔。模板形参表不能为空。

模板形参可以是表示类型的类型形参,也可以是表示常量表达式的非类型形参。非类型形参跟在类型说明符之后声明。类型形参跟在关键字 class 或 typename 之后定义,例如,class T 是名为 T 的类型形参,在这里 class 和 typename 没有区别。

使用函数模板时,编译器会推断哪个(或哪些)模板实参绑定到模板形参。一旦编译器确定了实际的模板实参,就称它实例化了函数模板的一个实例。实质上,编译器将确定用什么类型代替每个类型形参,以及用什么值代替每个非类型形参。推导出实际模板实参后,编译器使用实参代替相应的模板形参产生编译该版本的函数。编译器承担了为我们使用的每种类型而编写函数的单调工作。

	cout<<Compare(str1,str2);
00411479  lea         eax,[ebp-5Ch] 
0041147C  push        eax  
0041147D  lea         ecx,[ebp-34h] 
00411480  push        ecx  
00411481  call        Compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > (4111DBh) 
00411486  add         esp,8 
00411489  mov         esi,esp 
0041148B  push        eax  
0041148C  mov         ecx,dword ptr [__imp_std::cout (4192B4h)] 
00411492  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (4192B8h)] 
00411498  cmp         esi,esp 
0041149A  call        @ILT+340(__RTC_CheckEsp) (411159h) 
	cout << Compare(1, 0) << endl;
0041149F  mov         dword ptr [ebp-140h],0 
004114A9  mov         dword ptr [ebp-134h],1 
004114B3  mov         esi,esp 
004114B5  mov         eax,dword ptr [__imp_std::endl (4192BCh)] 
004114BA  push        eax  
004114BB  lea         ecx,[ebp-140h] 
004114C1  push        ecx  
004114C2  lea         edx,[ebp-134h] 
004114C8  push        edx  
004114C9  call        Compare<int> (4110FFh) 


可以看到程序编译完成之后是使用不同的类型来实例化 函数模板的。编译出来之后实际上是两个不同的函数。

inline 函数模板
函数模板可以用与非模板函数一样的方式声明为 inline。说明符放在模板形参表之后、返回类型之前,不能放在关键字 template 之前。

eg:
// ok: inline specifier follows template parameter list
template <typename T> inline T min(const T&, const T&);
// error: incorrect placement of inline specifier
inline template <typename T> T min(const T&, const T&);


2、类模板

按照C++ primer中的实现一个自己定义的deque类

template <class Type> class Queue {
public:
Queue (); // default constructor
Type &front (); // return element from head of Queue
const Type &front () const;
void push (const Type &); // add element to back of Queue
void pop(); // remove element from head of Queue
bool empty() const; // true if no elements in the Queue
private:
// ...
};

与调用函数模板形成对比,使用类模板时,必须为模板形参显式指定实参:


编译器使用实参来实例化这个类的特定类型版本。实质上,编译器用用户提供的实际特定类型代替 Type,重新编写 Queue 类。在这个例子中,编译器将实例化三个 Queue 类:第一个用 int 代替 Type,第二个用 vector<double> 代替 Type,第三个用 string 代替 Type。

可以给模板形参赋予的唯一含义是区别形参是类型形参还是非类型形参。如果是类型形参,我们就知道该形参表示未知类型,如果是非类型形参,我们就知道它是一个未知值

模板形参作用域
模板形参的名字可以在声明为模板形参之后直到模板声明或定义的末尾处使用。模板形参遵循常规名字屏蔽规则。与全局作用域中声明的对象、函数或类型同名的模板形参会屏蔽全局名字:
typedef double T;
template <class T> T calc(const T &a, const T &b)
{
// tmp has the type of the template parameter T
// not that of the global typedef
T tmp = a;
// ...
return tmp;
}
2.1 模板声明
像其他任意函数或类一样,对于模板可以只声明而不定义。声明必须指出函数或类是一个模板:

// declares compare but does not define it
template <class T> int compare(const T&, const T&) ;

2.2 每个模板类型形参前面必须带上关键字 class 或 typename,每个非类型形参前面必须带上类型名字,省略关键字或类型说明符是错误的:
// error: must precede U by either typename or class
template <typename T, U> T calc (const T&, const U&) ;



16.1.4. 模板类型形参
类型形参由关键字 class 或 typename 后接说明符构成。在模板形参表中,这两个关键字具有相同的含义,都指出后面所接的名字表示一个类型。模板类型形参可作为类型说明符在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同。具体而言,它可以用于指定返回类型或函数形参类型,以及在函数体中用于变量声明或强制类型转换。
// ok: same type used for the return type and both parameters
template <class T> T calc (const T& a, const T& b)
{
// ok: tmp will have same type as the parameters & return type
T tmp = a;
// ...
return tmp;
}



typename 与 class 的区别

在函数模板形参表中,关键字 typename 和 class 具有相同含义,可以互换使用,两个关键字都可以在同一模板形参表中使用:
// ok: no distinction between typename and class in template
parameter list
template <typename T, class U> calc (const T&, const U&);
使用关键字 typename 代替关键字 class 指定模板类型形参也许更为直观,毕竟,可以使用内置类型(非类类型)作为实际的类型形参,而且,typename更清楚地指明后面的名字是一个类型名。但是,关键字 typename 是作为标准C++ 的组成部分加入到 C++ 中的,因此旧的程序更有可能只用关键字 class。

在模板定义内部指定类型
除了定义数据成员或函数成员之外,类还可以定义类型成员。例如,标准库的容器类定义了不同的类型,如 size_type,使我们能够以独立于机器的方式使用容器。如果要在函数模板内部使用这样的类型,必须告诉编译器我们正在使用的名字指的是一个类型。必须显式地这样做,因为编译器(以及程序的读者)不能通过检查得知,由类型形参定义的名字何时是一个类型何时是一个值。例如,
考虑下面的函数:
template <class Parm, class U>
Parm fcn(Parm* array, U value)
{
Parm::size_type * p; // If Parm::size_type is a type, then a
declaration

// If Parm::size_type is an object, then
multiplication
}
我们知道 size_type 必定是绑定到 Parm 的那个类型的成员,但我们不知道 size_type 是一个类型成员的名字还是一个数据成员的名字,默认情况下,编译器假定这样的名字指定数据成员,而不是类型。如果希望编译器将 size_type 当作类型,则必须显式告诉编译器这样做:
template <class Parm, class U>
Parm fcn(Parm* array, U value)
{
typename Parm::size_type * p; // ok: declares p to be a pointer
}
通过在成员名前加上关键字 typename 作为前缀,可以告诉编译器将成员当作类型。通过编写 typename parm::size_type,指出绑定到 Parm 的类型的size_type 成员是类型的名字。当然,这一声明给用实例化 fcn 的类型增加了一个职责:那些类型必须具有名为 size_type 的成员,而且该成员是一个类型。
如果拿不准是否需要以 typename 指明一个名字是一个类型,那么指定它是个好主意。在类型之前指定 typename 没有害处,因此,即使 typename 是不必要的,也没有关系。


在某些情况下,不可能推断模板实参的类型。当函数的返回类型必须与形参表中所用的所有类型都不同时,最常出现这一问题。在这种情况下,有必要覆盖模板实参推断机制,并显式指定为模板形参所用的类型或值。

在返回类型中使用类型形参
指定返回类型的一种方式是引入第三个模板形参,它必须由调用者显式指定:
// T1 cannot be deduced: it doesn't appear in the function parameter
list
template <class T1, class T2, class T3>
T1 sum(T2, T3);
这个版本增加了一个模板形参以指定返回类型。只有一个问题:没有实参的类型可用于推断 T1 的类型,相反,调用者必须在每次调用 sum 时为该形参显式提供实参。

为调用提供显式模板实参与定义类模板的实例很类似,在以逗号分隔、用尖括号括住的列表中指定显式模板实参。显式模板类型的列表出现在函数名之后、实参表之前:
// ok T1 explicitly specified; T2 and T3 inferred from argument types
long val3 = sum<long>(i, lng); // ok: calls long sum(int, long)
这一调用显式指定 T1 的类型,编译器从调用中传递的实参推断 T2 和 T3的类型。
显式模板实参从左至右对应模板形参相匹配,第一个模板实参与第一个模板形参匹配,第二个实参与第二个形参匹配,以此类推。假如可以从函数形参推断,则结尾(最右边)形参的显式模板实参可以省略。如果这样编写 sum 函数:
// poor design: Users must explicitly specify all three template
parameters
template <class T1, class T2, class T3>
T3 alternative_sum(T2, T1);
则总是必须为所有三个形参指定实参:
// error: can't infer initial template parameters
long val3 = alternative_sum<long>(i, lng);
// ok: All three parameters explicitly specified
long val2 = alternative_sum<long, int, long>(i, lng);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值