C++模板详解——使用篇

假如我们需要取得两个变量中较大的变量,或许,我们可以通过重载的方式实现,如下。

int max(int fir,int sec);
float max(float fir,float sec);
double max(double fir,double sec);

有一天,我们定义了一个新的type,School,取决于max的实现,我们不仅需要重载School::operator<(), 或者School::operator>()还要重载一个新的max

const School max(School& fir,School& sec);

使用C++的模板,从此告别这些繁琐而又略显臃肿的代码。

注:1. 上述的返回值可以考虑使用const School&,但一般不建议,参见在返回值拒绝reference
2.形参使用了School&,参见传参时,使用引用替换变量

函数模板##

顾名思义,模板,也就是“模板”,并不是实际存在的东西,而只是为了让我们更方便地生产某些东西的模具。C++的模板分为了两类,类模板与函数模板。分别用于让我们方便地“生产各种各样的函数与类”,它们都使用了template,class,typename几个关键字。为什么说是各种各样,看完了博客自然就明白了。下面介绍函数模板。

示例####

template < class type>
type max(type fir,type sec)
{
    return fir > sec ? fir : sec; 
}

template告诉编译器这是一个模板,紧跟在后面的<>中声明了模板形参,这些形参在模板中可以充当类型,声明可以选用class或者typename,暂时认为两者在C++中作用相同。普通函数的形参为一个变量,模板形参为一种变量类型。也就是,我们可以通过指定模板形参的类型。来个简单的例子。
比如int,float来形成不同的重载函数

template < class type...>
type max(type fir,type sec...)
{
    return fir > sec ? fir : sec; 
}
void main()
{
    int a(1),b(2);
    float c(1),d(2);
    max(a,b);    //具现化int max(int fir,int sec);
    max(c,d);    //具现化int max(float fir,float sec); 
}

第一个max使用了int类型的参数,相当于告诉函数模板,type对应于int,在具现化的函数模板中,type的作用相当于int。所以具现化的函数相当于int
max(int fir,int sec);相对应地,使用了float调用函数模板,也就是制定了type为float,与前一个函数形成了重载。

注:虽然float能够隐私转换为int,但是还是会具现化新的函数。只有当前的参数类型与已经具现化的函数模板完全匹配的时候,才会继续使用已经具现化的函数。

拓展####

template < class type_1,class type_2...>
type_1 func(type_2 fir,type_1 sec,int thir)
{
    //return...
}

相对于前一个模板函数,这个模板函数的模板形参数量增加了,在普通的形参列表中,模板形参的顺序打乱了,还增加了int的形参。

  1. 在模板形参中,我们可以随意地定义任意数量的模板形参,但必须保证能够全部初始化。
  2. 使用了不同的类型名type_1,type_2...意味着我们可以指定多种类型的模板形参,其类型也可以不相同。
  3. 模板形参没有要求必须与普通函数形参一一对应,即在形参中的顺序可以随意打乱,其类型由相应的普通形参的类型决定。如,type_1的类型由sec的类型决定。
  4. 在模板函数中,除了模板形参外,可以使用内置的或者自定义的类型。

还是来个简单的例子

template < class type_1,class type_2>
void max(type_2 fir,type_1 sec,int)//最后的参数没有使用,可以直接忽略形参名
{
    std::cout<<fir<<"+"<<sec<<endl;
}
void main()
{
    int a(1);
    float b(1.0);
    max(a,b,1);                //1. 具现化void max(int fir,float sec,int);
}

第一个实参为int型,其对应的形参是type_2,所以type_2具现化后就是int。

第二个实参为float,其对应的形参type_1,所以type_1具现化后就是float。最后的具现化的函数就是int max(int fir,float sec,int);

指定参数类型####

还记得使用STL容器的方法吗,比如定义一个vector类型的容器。STL也叫作标准模板库,也就是其内部也是通过模板实现的,所以这种名称后加类型名的方法对我们也同样适用。

void Select(int a)
{
    std::cout<<"是int型"<<endl;
}
void Select(float a)
{
    std::cout<<"是float型"<<endl;
}
template < class type_1,class type_2>
void myPrint(type_1 fir,type_2 sec)
{
    Select(fir);
}
void main()
{
    myPrint(1.0,1);            //输出"是float型"
    myPrint<int>(1.0,1);       //输出"是int型"
}

在上面的例子中,我们可以发现:

  1. 在调用模板函数的时候,我们可以通过直接指定模板形参的类型从而阻止普通函数形参对于模板形参的影响。但是,指定的类型与普通函数形参必须能够进行类型转换。

比如,内置类型的int与double可以相互转换,所以myPrint< double>(1)可用。但是string与int之间不可相互转换myPrint< string>(1)就没办法通过编译。假如我们定义了class My,其构造函数为public:My(int),那么认为My与int可以相互转换(本质上是隐式调用了My的构造函数),myPrint< My>(1)就可以通过编译。

类模板##

假如你对函数模板还不会使用,请自行回顾,一些函数模板讲过的在下面不再赘述。

实例##

template < class type_1,class type_2>
class Student
{
public:
    Student(){}
    Student(type_1 fir,type_2 sec){}
    Student(type_1 fir){}
private:
    type_1 value_1;
    type_2 value_2;
    ...
};
void main()
{
    Student stu(1,1);               //error
    Student<int,float> stu(1,1);    //OK
}

template,class的作用与函数模板一致。不同的是:

  1. 类模板必须在使用的时候指定好模板形参的类型,编译器不会通过public接口,包括构造函数去作为模板形参类型的辨别依据。记得vector vec吧,没见过vector vec(1)吧。
  2. 使用类模板的时候,使用到的成员函数在主调语句必须可见。比如,上述的Student(type_1 fir,type_2 sec)在main中调用,其函数定义在main所在文件必须可见。再比如上述例子,假如其实现分配到如下几个文件,在链接的时候将出错。读者可以先记得,在“精通篇”会详细阐述这一点。
  3. 类模板中,慎用模板形参重载函数。上述的例子中,假如再增加Student(type_2)就会编译出错。编译器无法在Student(type_1)与Student(type_2)中做抉择。
//1.h
template < class type_1,class type_2>
class Student
{
public:
    ...
    Student()    //有具体实现的构造函数
    {
        ...
    }
    Student(type_1 fir,type_2 sec);
private:
    ...
};
//1.cpp
#include"1.h"
Student< class type_1,class type_2>::Student()
{}
//core.cpp
#include"1.h"
void main()
{
    Student<int,int> stu(1,1);    //构造函数定义在1.cpp中,不可见,出错
    Student<int,int> stu();       //默认构造函数随1.hinclude,可见,编译通过
}

C++中模板的基本使用方法如上。下一篇博客将带大家进入模板特化以及深入解释上述例子无法编译的原因。

posted @ 2015-10-23 20:33  followDreamLgx  阅读(866)  评论(0编辑  收藏  举报