Function Templates 函数模板——初窥

1.1Function Template初窥

所谓的function templates是指由参数化手段表现一整个族群的function。

一个function templates可以表示一族(一整群)functions,其表现和一般的function并无二致,只是其中某些元素在编写时尚未确定,换而言之,那些[尚未确定的元素]被[参数化]了。

1.1.1定义Template
下面的function templates传回两个数值中的较大者:

template<typename T>
inline T 
const & max( T const & a, T const & b )
{
    
return a < b ? b : a
}

这一份template定义式代表了一整族functions,他们的作用都是传回a和b两参数中的较大者。两个参数的型别都尚未确定,我们说它"template parameter T"。如你所见,template parameters必须以如此形式加以宣告:

 template<以逗号分隔的参数列>
上述例子中,参数列就是typename T。请注意,例子中的[<]和[>]在这里被当作尖括号使用。关键字typename引入了一个所谓的type parameter(型别参数)------这是目前为止C++程式中最常使用的一种template parameter,另还存在其他种类的template parameter(如:nontype parameter,[非型别参数]),这个以后讨论。

此处的型别参数是T,你也可以使用其他任何标识符号(identifier)来表示型别参数,但习惯写成T(译注:代表Type)。Type parameter可标识任何型别,在function templates被呼叫时,经由传输具体型别而使T得以被具体指定。你可以使用任何型别,只要它支持T所要完成的操作。本列中型别T必须支援operator<以比较两值大小。

由于历史因素,你也可以使用关键字class代替关键字typename来定义一个type parameter。关键字typename是C++发展晚期才引进的,在此之前只能经由关键字class引入type parameter。关键字class目前依然可以用。因此template max()也可以被写成如下等价形式:

template<class T>
inline T 
const & max(T const & a, T const & b)
{
    
return a < b ? b : a;
}

就语义而言,前后两者毫无区别。即便使用关键字class,你还是可以把任意型别(包括non-class型别)当作实际的template arguments。但是这么写可能带来一些误导(让人误以为T必须是class型别),所以最好还是使用关键字typename。请注意,这和class的型别宣告并不是同一回事:宣告type parameters时我们不能把关键字typename换成关键字struct。

1.1.2使用Template
以下程式示范如何使用max() function template:

#include <iostream>
#include 
<string>
#include 
"max.hpp"

int main()
{
    
int i = 42;
    std::cout
<<"max(7,i): "<<::max(7,i)<<std::endl;
 
    
double f1 = 3.4;
    
double f2 = -6.7;
    std::cout
<<"max(f1,f2): "<<::max(f1,f2)<<std::endl;

    std::
string s1 = "mathematics";
    std::
string s2 = "math";
    std::cout
<<"max(s1,s2): "<<::max(s1,s2)<<std::endl;
}

程序呼叫了max()三次。第一次参数是两个int,第二次参数是两个double,最后一次参数是两个std::string。每一次max()均比较两值取大者。程序运行结果为:

max(7,i): 42
max(f1,f2): 
3.4
max(s1,s2): mathematics

注意程序对max()的三次呼叫都加了前缀符号"::",以遍确保被唤起的是我们在全域名空间(global namespace)中定义的max()。标准程序库内也有一个std::max() template,可能会在某些情况下被唤起,或在呼叫时引起模棱两可(ambiguity,歧义性,如果某个引数的型别定义于namespace std中,例如string,根据C++搜寻规则,::max()和std::max()都会被找到,那就会引起歧义性)。

 一般而言,templates不会被编译为[能够处理任意型别]的单一实物(entity),而是被编译为多个个别实物,每一个处理某一特定型别。([一份实物,适用所有型别],理论上成立,实际不可行。毕竟所有语言规则都奠基于[将会产生出不同实物]的概念all language rules are based on the concept that different entities are generated)。因此,针对三个型别,max()被编译成三个实物。

例如第一次呼叫max():
int i = 42;
...max(
7,i)...

使用的是[以int为template parameter T]的funciton template,语义上等同于呼叫以下函数:

inline int const & max(int const & a, int const & b)
{
    
return a < b ? b : a;
}

以具体型别替换template parameters的过程称为[具现化](instantiation,或称[实体化])。过程中会产生template的一份实体(instance)。不巧的是,instantiation和instance这两个术语在OO(面向对象)编程领域中有其他含义,通常用来表示一个class的具体物件(concrete object)。

注意,只要function template被使用,就会自动引发具现化过程。程序员没有必要个别申请具现过程。

类似情况,另两次对max()的呼叫被具现化为:

const double & max(double const &double const &);
const std::string & max(std::string const &, std::string const &);

 如果试图以某个型别来具现化function template,而该型别并未支援function template中用到的操作,就会导致编译错误。例如:

std::complex<float>c1, c2;  //此型别并不提供operator<
... 
max(c1,c2);                 
//编译期出错

实际上,template会被编译两次:

1.不具现化,只是对template程序代码进行语法检查以发现诸如[缺少分号;]等等的语法错误。

2.具现化,编译器检查template程序代码中的所有呼叫是否合法,诸如[未授支援函数调用]便会在这个阶段被检查出来。

这会导致一个严重问题:当function template被运用而引发具现化过程时,某些时候编译器需要用到template的原始定义。一般情况下,对普通的(non-template)functions而言,编译和连结两步骤是各自独立的,编译器只检查各个functions的宣告式是否和呼叫式相符,然后template的编译破坏了这个规则。眼下我们用最简单的解法:把template程序代码以inline形式写在表头档(header)中。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章