模板与泛型编程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wk_bjut_edu_cn/article/details/82118722

定义模板

函数模板

1.实例化函数模板

当我们调用一个函数模板时,编译器(通常)用函数实参来推断模板实参。

cout<<compare(1,0)<<endl;//推断实参类型为int

2.非类型模板参数

除了定义类型参数,还可以在模板中定义非类型参数。一个非类型参数表示一个值而非一个类型。我们通过一个特定的类型名而非关键字class或typename来指定非类型参数。

一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。绑定到非类型整形参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期。我们不能用一个普通的局部变量或动态对象作为指针或引用非类型模板参数的实参。

template<unsigned N,unsigned M>
int conpare(const char (&p1)[N],const char (&p1)[M])
{
    return strcmp(p1,p2);
}

3.模板编译

与普通函数和类的成员函数不同,为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义。因此,与非模板代码不同,模板的头文件通常既包括声明也包括定义。 

类模板

1.与函数模板的不同之处是,编译器不能为类模板推断模板参数类型。为了使用类模板,必须在模板名后的尖括号中提供额外信息。

2.类模板成员函数的实例化

对于一个实例化了的类模板,其成员只有在使用时才能被实例化。

模板参数

1.默认情况下,C++假定通过作用域运算符访问的名字不是类型。因此,如果我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。通过关键字typename做到这一点。

模板实参推断

1.类型转换与模板类型参数

如果一个函数形参的类型使用了模板类型参数,那么它采用特殊的初始化规则。只有很有限的几种类型转换会自动地应用于这些实参。

P601页,看一下

2.指定显式模板实参

template <typename T1,typename T2,typename T3>
T1 sun(T2,T3);

在本例中,没有任何函数实参的类型可用来推断T1的类型。每次调用sum时调用者都必须为T1提供一个显式模板实参。

//T1是显式指定的,T2和T3是从函数实参类型推断而来的
auto val3=sum<long long>(i,lng);

3.正常类型转换应用于显式指定的实参

函数模板可以用普通类型定义的参数,即不涉及模板类型参数的类型。这种函数实参不进行特殊处理,它们正常转换为对应形参的类型。

对于模板类型参数已经显式指定了的函数实参,也可进行正常的类型转换

template <typename T>
int compare(const T &v1,const T &v2)
{
    if(v1<v2) return -1;
    if(v2<v1) return 1;
    return 0;
}
long lng;
compare(lng,1024);    //错误:模板参数不匹配
compare<long>(lng,1024);    //正确;实例化compare(long,long)
compare<int>(lng,1024);    //正确:实例化compare(int,int)

4.右值引用参数

我们可以将任意类型的实参传递给T&&类型的函数参数。对于这种类型的参数,可以传递给它右值,也可以传递给它左值。如果将一个左值传递给这样的参数,则函数参数被实例化为一个普通的左值引用。

template <typename T>void f3(T&&);
f3(42);    //实参是一个int类型的右值;模板参数T是int
int i=5;
//当将一个左值传递给f3的(右值引用)函数参数时,
//编译器推断T为一个左值引用类型
f3(i);    //实参是一个左值;模板参数T是int&
//此时,f3(i)的实例化结果可能如下
void f3<int &>(int& &&);
//折叠之后P609,转换如下
void f3<int&>(int&);

重载与模板

1.函数模板可以被另一个模板或一个普通非模板函数重载。与往常一样,名字相同的函数必须具有不同数量或类型的参数。

对于一个调用,如果一个非函数模板与一个函数模板提供同样的匹配,则选择非函数模板。

可变参数模板

1.一个可变参数模板就是一个接受可变数目参数的模板参数或模板类。可变数目的参数被称为参数包。存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数。

我们用一个省略号来指出一个模板参数或函数参数表示一个包。

//Args是一个模板参数包;rest是一个函数参数包
//Args表示零个或多个模板类型参数
//rest表示零个或多个函数参数
template <typename T,typename... Args>
void foo(const T &t,const Args... reset);
int i=0;double d=3.14;string s="hhh";
foo(i,s,42,d);    //包中有三个参数
foo(s,42,"hi");    //包中有两个参数
foo("hi");    //空包

 编译器会为foo实例化出三个不同的版本:

void foo(const int&,const string&,const int&,const doubble&);
void foo(const string&,const int&,const char[3]&);
void foo(const char[3]&);

模板特例化

1.当定义函数模板的特例化版本时,我们本质上接管了编译器的工作。即,我们为原模板的一个特殊实例提供了定义。一个特例化版本本质上是一个实例,而非函数名的一个重载版本。因此,特例化不影响函数匹配。

猜你喜欢

转载自blog.csdn.net/wk_bjut_edu_cn/article/details/82118722