您的位置:首页 > 编程语言 > C语言/C++

【C++】浅谈对模板的认识

2017-10-23 22:42 441 查看
如果要写出适合于所有类型的函数,你会怎么写?

      首先想到的应该就是函数重载了吧,函数重载使非常容易想到的,但是却存在许多缺点。比如:只要有新的类型出现,就需要添加新的函数;如果函数只有返回值不同,函数重载就不能解决了,除此之外,还有函数体都相同,仅有类型不同,代码显得冗杂;不方便维护等问题。

      其次,我们还可以想到借助公共基类,将通用的代码放到基类中,但是这样仍然难以维护代码,而且会失去类型检查这一优点。

      此外,还有宏函数业可以完成,但同样,不会进行类型检查,安全性不够。

      在C++中,我们有一个很好的办法可以达到目的,那就是模板了。编写与类型无关的逻辑代码,我们称之为泛型编程,而模板就是泛型编程的基础。

模板关键字:template,可以分为函数模板和类模板。

一. 函数模板

1.函数模板的格式:

template <typename 模板形参名字>

返回值类型 函数名(参数列表)

{}

例如:

template <typename T>
T Max(const T& a, const T& b)
{
return a > b ? a : b;
}
注意:(1).typename是定义模板的关键字,可以用class,但是不可以使用struct代替class

           (2).inline关键字也可以被模板使用,放置于模板参数列表之后,函数返回值之前。

例如:

template <typename T>
inline T Max(const T& a, const T& b)
{
return a > b ? a : b;
}
2.函数模板编译

(1).模板本身不是一个类或者函数,编译器用模板产生指定类型的函数或类,这一过程称之为模板实例化。

(2)模板在编译时可以理解为编译了两次,第一次是实例化之前,检查代码是否存在语法错误;第二次是在实例化期间,检查模板代码,查看是否所有的调用都有效。

注意:(1)隐式实例化中,模板中不会出现类型转换,只会产生新的实例。例如:

cout << Max(1, '2') << endl; //编译器将会报错
 当出现自己写的代码时,将会调用自己写的代码,模板将不会实例化,例如:
int Max(const int& a, const int& b)
{
return a > b ? a : b;
}
cout << Max(1, 2) << endl;//将会调用自己写的代码,不调用模板实例化的函数
cout << Max(1, '2') << endl;//将不会报错,调用自己写的代码,发生类型转换
        (2)显示实例化中,将只会调用模板实例化生成的函数。Max<>(1,2);  Max<int>(1,2);

        (3)编译器将会执行两种转换:一种是接受const的函数,可以用非const对象的引用或指针调用;另一种是数组或函数到指针的转换。

3. 函数模板的参数

(1)类型形参

    a. 模板形参名字只能在模板形参之后到模板的定义或声明之间使用,遵循名字屏蔽,例如:



   b. 模板形参的名字在同一个模板形参中只能出现一次

template <typename T, class T>//重定义 模板 参数“T”
  c. 函数模板内部不能指定缺省模板实参,类模板中可以使用默认参数
template <typename T=int >//在VS2013中经过编译器优化,可以编译通过
void Test(const T& t)
{
cout << typeid(t).name() << endl;
}
(2)非模板类型参数

非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数

template <typename T,int N>
void Funtest(T(&_arr)
)//数组的引用
{
for (int i = 0; i < N; i++)
_arr[i] = i;
}
4. 模板函数重载

所有重载版本的生命都应放于该函数被调用的位置之前。

5. 模板函数的特化

对于上述例子,比较两个参数中较大的一个,并不能适用于字符串,因此,我们需要对于字符串这种类型进行特化。

(1)关键字template<>

(2)函数名后一对尖括号,尖括号内指定特化定义的模板形参

(3)函数形参表中注意const的位置

例如:

template <typename T>
T Max(const T& a, const T& b)
{
return a > b ? a : b;
}
template<>//const修饰的是a,a的指向不能被改变,而const char* & b中,const修饰的是*a,a指向的空间的值不能被改
char* Max<char*>(char* const& a, char* const& b)
{
if (strcmp(a, b)==1)
return a;
return b;
}
二. 模板类

模板类同模板函数类似,关键字为template

template<typename 形参1, typename 形参2>

class 类名

{};

我们写过的模板顺序表链表就是应用模板类的

模板类的特化可以分为:全特化,偏特化

注意:偏特化并不仅仅是特化部分参数,而应该是针对模板参数更进一步的条件限制出来的一个特化版本。

三. 模板的分离编译

分离编译就是将函数的实现与调用不在同一个文件中,当模板分离编译时,在编译阶段没有问题,但是程序最终却会出现问题。

因为在分离式的编译环境下,编译器分别编译不同的cpp文件时,遇到未解决的符号时,在最终链接时会得到解决。而遇到模板时,模板仅在需要的时候才会实例化,因此,当实现该模板的cpp文件中没有用到模板实例时,编译器并不会实例化,工程中的obj文件中没有模板实例的二进制代码,连接器没办法处理为解决的符号,因此会报出错误。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 模板