模板和泛型编程

第一次看,看第一节即可

1.我们可以为函数定义一个模板,而不是为每一个类型定义一个函数。

比较函数:

  1. #include <iostream>
  2. template <typename T>
  3. int compare(const T&v1, const T&v2)
  4. {
  5. if(v1 < v2)
  6. return -1;
  7. if(v1 > v2)
  8. return 1;
  9. return 0;
  10. }
  11. int main()
  12. {
  13. int i1 = 10, i2 = 20;
  14. double d1 = 20.1, d2 = 20.01;
  15. unsigned u1 = 10, u2 = 10;
  16. std::cout << compare(i1, i2) << std::endl;
  17. std::cout << compare(d1, d2) << std::endl;
  18. std::cout << compare(u1, u2) << std::endl;
  19. }

模板定义以关键字template开始,

后面跟一个参数列表,这是一个逗号分隔的一个或多个模板参数的列表,用< >包围起来。

当我们调用一个函数模板时,编译器会用函数的实参来为我们推断模板实参

2.类型参数

类型参数前必须使用关键字class或者typename

在模板参数列表中,这两个关键字的含义相同,可以互换使用,一个模板参数列表中可以同时使用这两个关键字

类型参数在函数中使用我们就把它当作正常类型使用就可以了,比如作为函数的参数,返回值,以及函数内部定义变量等等

3.非类型模板参数

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

当一个模板被实例化,非类型参数被一个编译器推断的值所取代。

一个非类型参数可以是一个整形,或者是一个指向对象或函数类型的指针或(左值)引用,绑定到非类型整形参数必须是一个常量,绑定到指针或引用的非类型模板

参数的实参必须有静态生存期。

注意:非类型模板参数的模板必须是常量表达式

inline 和constexpr也可以修饰函数模板,放在返回值前面

4.编写类型无关的代码

编写泛型代码的两个重要的原则

<1.模板中的函数参数是const的引用

<2.函数体条件中判断仅仅使用<比较

这两个可以降低对类型的要求

比如const的引用,就保证了函数可以用于不能拷贝的类型。

<比较就可以用于大多数定义了<比较的类型。

结论:模板程序应该尽量减少对实参类型的要求

5.模板编译

模板编译和普通的编译不同,它是在使用实例化时编译器才生成代码,为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,

因此和非模板代码不同,模板的头文件通常既包括声明又包括定义。

模板的设计者应该提供一个头文件,包含模板定义以及在类模板或成员定义中用到的所有名字的声明。

模板的用户必须包含模板的头文件,以及用来实例化模板的任何类型的头文件。

6.大多数编译错误在实例化期间报告

保证传递给模板的实参支持模板所要求的操作,以及这些操作在模板中能正确工作,是调用者的责任。

例题:编写行为类似find算法的模板,函数需要两个模板类型参数,一个表示函数的迭代器参数,另外一个表示值的类型。

  1. #include <iostream>
  2. #include <string>
  3. #include <list>
  4. #include <vector>
  5. template<typename iterator, typename T>
  6. iterator Find(const iterator &beg, const iterator &end, const T&ele)
  7. {
  8. for(auto it = beg; it != end; ++it)
  9. {
  10. if(*it == ele)
  11. return it;
  12. }
  13. return end;
  14. }
  15. int main()
  16. {
  17. std::vector<int>ivec1 = {1,2,3,4,5,6,7,8,9,0};
  18. auto ret = Find(begin(ivec1), end(ivec1), 3);
  19. if(ret == end(ivec1))
  20. std::cout << "not find" << std::endl;
  21. else
  22. std::cout << "find " << *ret << std::endl;
  23. std::list<std::string>il = {"111","222","333","444","555"};
  24. auto ret2 = Find(il.begin(), il.end(), "333");
  25. if(ret2 == il.end())
  26. std::cout << "not find" << std::endl;
  27. else
  28. std::cout << "find " << *ret2 << std::endl;
  29. }

2.类模板

1.一个类模板的每个实例都形成一个独立的类,但是类和类之间没有关系。

<2.我们既可以在类模板外部为其定义成员函数,也可以在类模板内部定义,内部定义被隐式的声明为内敛函数。

<3.类模板的成员函数具有和模板相同的模板参数,因此,定义在模板之外的成员函数就必须以关键字template开始,后接参数列表.

<4.默认情况下,对于一个实例化的类模板,其成员只有在使用时才被实例化。

<5.当我们使用一个类模板类型必须提供模板参数,但有一个例外,在类模板自己的作用域中,我们可以直接使用模板名而不提供实参。

模板类和友元

<6.类和友元各自是否是模板是无关的。友元关系被限定在用相同的类型实例化。

<7.一个模板类也可以指定另外一个模板类为友元或者另外一个模板类的特定实例

<9.类模板别名(c++11)

旧标准中,我们只能使用typedef 为特定模板类型定义类型别名。

c++11标准中,我们可以为模板类定义模板类型别名,而且多个参数时可以指定参数是T或者是特定类型。

template <typename T, typename X, typename Z> using TEP = C<T, X, Z>;

TEP<int, int, int> t;

<10.类模板的static成员

当类模板包含static成员时(数据或者是函数),每个类模板的实例都有一个static成员,而且定义类模板的static成员时,前面必须加template <参数列表>

3.模板参数

<1.模板参数和作用域

模板参数符合隐藏规则,而且一个模板参数名在参数列表中只能出现一次。

一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置,出现于任何使用这些模板的代码之前。

<2.使用类的类型成员

我们处理模板的时候,必须让编译器知道名字是否表示一个类型

注意:

记住typename和class不是完全相同的,在指明是一个类型而不是名字的情况下必须用typename

比如typedef typename T::size_type = size_type

出处:http://blog.csdn.net/wwh578867817/article/details/43306513

时间: 2024-08-06 15:58:50

模板和泛型编程的相关文章

C++ 模板与泛型编程

<C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异. 在泛型编程中,我们所编写的类和函数能够多态地用于跨越编译时不相关的类型.一个类或一个函数可以用来操纵多种类型的对象. 面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数式多态性. 模板是泛型

C++ Primer 学习笔记_77_模板与泛型编程 --实例化

模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化. 1.类的实例化 当编写Queue<int>qi时,编译器自己主动创建名为Queue<int>的类.实际上,编译器通过又一次编写Queue模板,用类型int取代模板形參的每次出现而创建Queue<int

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_76_模板和泛型编程 --模板定义[继续]

模板和泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示一个类型. 模板类型形參可作为类型说明符在模板中的不论什么地方,与内置类型说明符或类类型说明符的使用方式全然同样. 详细而言,它能够用于指定返回类型或函数形參类型,以及在函数体中用于变量声明或强制类型转换. template <class T> T calc(const T &a,cons

C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模板的定义. 泛型编程与面向对象编程一样,都依赖于某种形式的多态性.面向对象编程中的多态性在执行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异.仅仅要使用基类的引用或指针,基类类型或派生类类型的对象就能够使用同样的代码. 在泛型编程中,我们所编写的类和函数能够

C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一些情况下,能够利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数. compare函数和 Queue类都是这一问题的好样例:与C风格字符串一起使用进,它们都不能正确工作. compare函数模板: template <typename Type> int compare(cons

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下

C++ primer 模板与泛型编程

继续浏览c++ primer 看到模板与泛型编程这章,就顺便把这几节的代码综合了下,对一个Queue队列模板的实现 贴一下代码(看完书,自己敲,忘记了哪再看下书) #include <ostream> using std::ostream; //声明Queue的模板类 template <class Type> class Queue; //声明模板函数 template <class T> ostream& operator<<(ostream&a

C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还可以只特化push和pop成员.我们将特化push成员以复制字符数组,并且特化pop成员以释放该副本使用的内存: template<> void Queue<const char *>::push(const char *const &val) { char *new_item = new char[sizeof(val) + 1]; strncpy(new_item,val,sizeof(