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

xiaoxiao2021-02-28  15

如果要写出适合于所有类型的函数,你会怎么写?

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

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

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

      在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)[N])//数组的引用 { 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文件中没有模板实例的二进制代码,连接器没办法处理为解决的符号,因此会报出错误。

转载请注明原文地址: https://www.6miu.com/read-1900076.html

最新回复(0)