之前已经总结过函数模板和类模板了,对于模板还有一些其他的特性,这篇主要介绍这些特性.主要都是一些特殊情况.
模板的其他特性
1.缺省参数
(1)类模板的模板参数可以带有缺省值,实例化该模板时,如果提供了相应的实参,则忽略缺省值,反之则以缺省作为对应形参的值
(2)如果某个模板参数带有缺省值,那么它后面的所有参数都必须带有缺省值
(3)C++98不允许为函数模板的参数指定缺省值<尖括号里面的>, c++11允许
    编译的时候需要加上 -std=c++11
(4)对于函数模板,如果模板参数的缺省值与隐式推断的类型不一致,以隐式推断的类型为准,忽略其缺省值.==>c++11标准下.

  1. #include <iostream>
  2. #include <typeinfo>
  3. using namespace std;
  4.  
  5. template<typename A = int,typename B = double, typename C = string>
  6. //template<typename A,typename B = double, typename C = string> //ok
  7. //template<typename A = int,typename B, typename C = string> //error
  8. //template<typename A = int,typename B = double, typename C> //error
  9. class X {
  10. public:
  11. static void foo (void) {
  12. cout << typeid (A).name () << ' ' << typeid (B).name () << ' ' << typeid (C).name () << endl;
  13. }
  14. };
  15.  
  16. int _x = 200;
  17. void foo (int x, int y = /*x*/_x) {
  18. cout << x << ' ' << y << endl;
  19. }
  20.  
  21. template<typename A, typename B = A>
  22. //template<typename A = B, typename B = int> //error
  23. class Y {
  24. public:
  25. static void foo (void) {
  26. cout << typeid (A).name () << ' ' << typeid (B).name () << endl;
  27. }
  28. };
  29. template<typename A = int, typename B = double, typename C = string>
  30. void fun (void) {
  31. cout << typeid (A).name () << ' ' << typeid (B).name () << ' ' << typeid (C).name () << endl;
  32. }
  33. template<typename T = int>
  34. void bar (T arg) {
  35. cout << typeid (arg).name () << endl;
  36. }
  37. int main (void) {
  38. X<char, short, long>::foo (); // c s l
  39. X<char, short>::foo (); // c s Ss
  40. X<char>::foo (); // c d Ss
  41. // X<>::foo (); // i d Ss
  42. foo (100);
  43. // PUSH ... 100
  44. // PUSH ... _x
  45. Y<long long>::foo ();
  46. // Y<>::foo ();
  47. fun<char, short, long> ();
  48. fun<char, short> ();
  49. fun<char> ();
  50. fun<> ();
  51. fun ();
  52. bar (1.23);
  53. return 0;
  54. }
2.非类型参数
(1)模板处理可以接受类型参数意外,也可以接受非类型参数,即数值参数,非类型参数不能用typename声明,而要注明其具体类型,而且传递给模板非类型参数的实参必须是常量,常量表达式,或者带有常属性(const限定)的变量,但是不能同时具有挥发性(volatile限定).
  1. #include <iostream>
  2. #include <iomanip>
  3. using namespace std;
  4.  
  5. template<typename T = int, size_t S = 3>
  6. class Array {
  7. public:
  8. T& operator[] (size_t i) { return m_a[i]; }
  9. T const& operator[] (size_t i) const { return const_cast<Array&> (*this)[i]; }
  10. T m_a[S];
  11. };
  12.  
  13. int square (int x) { return x * x; }
  14.  
  15. template<int x> int square (void) {
  16. return x * x;
  17. }
  18.  
  19. /*
  20. int square (void) { return 100;}
  21. */
  22.  
  23. int main (void) {
  24. Array<int> a;
  25. /*
  26. a.m_a[0] = 10;
  27. a.m_a[1] = 20;
  28. a.m_a[2] = 30;
  29. */
  30. a[0] = 10; // a.operator[] (0) = 10;
  31. a[1] = 20;
  32. a[2] = 30;
  33. Array<int> const b = a;
  34. // cout << b.m_a[0] << ' ' << b.m_a[1] << ' ' << b.m_a[2] << endl;
  35. cout << b[0] << ' ' << b[1] << ' ' << b[2] << endl;
  36. Array<string> c;
  37. c[0] = "北京";
  38. c[1] = "上海";
  39. c[2] = "广州";
  40. cout << c[0] << ' ' << c[1] << ' ' << c[2] << endl;
  41. int const /*volatile */x = 2, y = 1;
  42. Array<Array<int, x+y+1>, 1+1+1> d;
  43. // Array<Array<int, 2+1+1>, 1+1+1> d;
  44. for (int i = 0; i < 3; ++i)
  45. for (int j = 0; j < 4; ++j)
  46. d[i][j] = i * 4 + j + 1;
  47. // d.operator[](i).operator[](j) = ...;
  48. for (int i = 0; i < 3; ++i) {
  49. for (int j = 0; j < 4; ++j)
  50. cout << setw (2) << d[i][j] << ' ';
  51. cout << endl;
  52. }
  53. Array<Array<Array<int> > > e;
  54. cout << square (10) << endl;
  55. cout << square<10> () << endl;
  56. return 0;
  57. }
(2)向模板传递字符串形式的非类型参数:形参必须使用字符指针,实参必须使用非静态全局字符数组
(3)模板的浮点型参数只能使用整数类型???
 
3.typename关键字
(1)在模板参数被实例化之前,模板参数所表示的具体类型并不确定,编译器会把依赖于模板参数的嵌套(内部)类型理解为某个类的静态成员变量,当其看到用该变量定义其他变量的代码时,会报告错误.
(2)typename关键字可以告诉编译器,其后的标识符不是静态成员变量,而是某种类型,编译器就会将其有关该编译的类型检查推迟到模板实例化过程中,避免编译错误 ----- 解决嵌套依赖.
class关键字:1)声明类 2)声明模板的类型实参
typename关键字:1)解决嵌套依赖  2)声明模板的类型参数
struct关键字:声明类
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class A {
  5. public:
  6. typedef unsigned int uint;
  7. class B {};
  8. };
  9.  
  10. template<typename T> void foo (void) {
  11. typename T::uint u;
  12. typename T::B b;
  13. }
  14. void bar (void) {}
  15.  
  16. int main (void) {
  17. A::uint u;
  18. A::B b;
  19. foo<A> ();
  20. return 0;
  21. }
4.template关键字作用
(1)声明函数模板和类型模板
(2)解决嵌套模板:
依赖于模板参数的类型的内部模板
在模板代码中,通过依赖于模板参数的对象,引用或指针,访问其带有模板特性的成员需要使用template关键字显示指明其后的名称是一个模板,避免编译器将模板参数表的左右尖括号理解为小于号和大于号,导致编译失败
  1. #include <iostream>
  2. #include <typeinfo>
  3. using namespace std;
  4.  
  5. template<typename T> class A {
  6. public:
  7. // 嵌套模板函数
  8. template<typename U>
  9. void foo (void) const {
  10. U var;
  11. cout << typeid (m_var).name () << ' ' << typeid (var).name () << endl;
  12. }
  13. private:
  14. T m_var;
  15. };
  16.  
  17. template<typename T> void bar (T const& a, T const* b) {
  18. a.template foo<double> ();
  19. b->template foo<double> ();
  20. }
  21.  
  22. int main (void) {
  23. A<int> a;
  24. a.foo<double> ();
  25. bar (a, &a);
  26. return 0;
  27. }
5.子类访问基类模板
在子类模板中访问那些在基类模板中声明且依赖于模板参数的符号,应该在前面加上作用域限定符::,或显示使用this指针,否则编译器将试图在全局域中寻找该符号,引发错误.
  1. #include <cstdlib>
  2. #include <iostream>
  3. using namespace std;
  4. class A {
  5. public:
  6. int m_var;
  7. void foo (void) const {}
  8. class X {};
  9. };
  10. class B : public A {
  11. public:
  12. void bar (void) const {
  13. cout << /*A::*/m_var << endl;
  14. /*A::*/foo ();
  15. /*A::*/X x;
  16. }
  17. };
  18. template<typename T>
  19. class C {
  20. public:
  21. int m_var;
  22. void foo (void) const {}
  23. class X {};
  24. void exit (int status) const {
  25. cout << "再见!" << endl;
  26. }
  27. };
  28. template<typename T>
  29. class D : public C<T> {
  30. public:
  31. void bar (void) const {
  32. // cout << C<T>::m_var << endl;
  33. cout << this->m_var << endl;
  34. // C<T>::foo ();
  35. this->foo ();
  36. typename C<T>::X x;
  37. // C<T>::exit (0);
  38. this->exit (0);
  39. }
  40. };
  41. int main (void) {
  42. B b;
  43. b.bar ();
  44. D<int> d;
  45. d.bar ();
  46. return 0;
  47. } 
6.模板型模板参数
类模板的模板参数如果结合的实参不是具体类型而是另外一个模板,那么不能使用typename关键字声明该参数,而是写明其所结合的类模板实参的原型:
template<模板形参表> class 模板性参数参数名
  1. #include <iostream>
  2. using namespace std;
  3.  
  4. template<typename T> class Array {
  5. public:
  6. void pushBack (T const& data) {
  7. cout << "向数组尾端压入数据" << endl;
  8. }
  9. void popBack (void) {
  10. cout << "从数组尾端弹出数据" << endl;
  11. }
  12. };
  13.  
  14. template<typename T> class List {
  15. public:
  16. void pushBack (T const& data) {
  17. cout << "向链表尾端压入数据" << endl;
  18. }
  19. void popBack (void) {
  20. cout << "从链表尾端弹出数据" << endl;
  21. }
  22. };
  23. template<typename T,template<typename> class C> class Stack {
  24. public:
  25. void push (T const& data) {
  26. m_c.pushBack (data);
  27. }
  28. void pop (void) {
  29. m_c.popBack ();
  30. }
  31. private:
  32. C<T> m_c;
  33. };
  34.  
  35. int main (void) {
  36. Stack<int, Array> sia;
  37. sia.push (100);
  38. sia.pop ();
  39. Stack<int, List> sil;
  40. sil.push (200);
  41. sil.pop ();
  42. return 0;
  43. }
7.嵌套模板的外部定义
如果将嵌套于一个类模板内部模板防盗包装模板的外部定义,需要按照作用域层次顺序,从外到内,从前到后依次使用独立template字句声明其模板参数表
 
8."零"初始化
基本类型不存在缺省构造函数,所以未被显示初始化的局部变量和成员变量具有一个未定义的初值.如果希望模板函数或者模板类中所有参数化类型的变量,无论是基本类型还是类类型,都能以缺省方式呗初始化,就必须显示进行缺省构造(写出构造函数),即"零"初始化.
  1. #include <iostream>
  2. using namespace std;
  3. template<typename T>
  4. void foo (void) {
  5. T var = T ();
  6. // int var = int ();
  7. // string var = string ();
  8. // Integer var = Integer ();
  9. cout << '[' << var << ']' << endl;
  10. }
  11. template<typename T>
  12. class Foo {
  13. public:
  14. Foo (void) : m_var () {}
  15. T m_var;
  16. };
  17. class Integer {
  18. public:
  19. Integer (int arg = 0) : m_var (arg) {}
  20. friend ostream& operator<< (ostream& os,Integer const& i) {
  21. return os << i.m_var;
  22. }
  23. private:
  24. int m_var;
  25. };
  26. int main (void) {
  27. /*
  28. foo<int> ();
  29. foo<string> ();
  30. foo<Integer> ();
  31. */
  32. Foo<int> fi;
  33. cout << '[' << fi.m_var << ']'<< endl;
  34. Foo<string> fs;
  35. cout << '[' << fs.m_var << ']' << endl;
  36. Foo<Integer> fn;
  37. cout << '[' << fn.m_var << ']' << endl;
  38. return 0;
  39. }
9.虚函数和多态
(1)类模板中可以声明虚函数,而且只要实例化该模板时所提供的类型实参不违背函数有效覆盖的条件,就可以形成多态
(2)由于模板函数的延迟编译要晚于类或者类模板中虚表的构建,因此模板函数不能同时又是虚函数.
不能被声明为虚函数的函数:
全局函数,静态成员函数,构造函数,模板性成员函数
  1. #include <iostream>
  2. using namespace std;
  3. template<typename A, typename B>
  4. class X {
  5. public:
  6. // 模板中的虚函数
  7. virtual A foo (B arg) const {
  8. cout << "X::foo" << endl;
  9. return A ();
  10. }
  11. // 不可以声明虚模板函数
  12. /*
  13. template<typename C>
  14. virtual void bar (void) const {}
  15. */
  16. };
  17. template<typename A, typename B, typename C, typename D>
  18. class Y : public X<C, D> {
  19. public:
  20. A foo (B arg) const {
  21. cout << "Y::foo" << endl;
  22. return A ();
  23. }
  24. };
  25. int main (void) {
  26. Y<int, double, int, double> y1;
  27. X<int, double>& x1 = y1;
  28. x1.foo (1.2);
  29. Y<int, double, int, char> y2;
  30. X<int, char>& x2 = y2;
  31. x2.foo ('A');
  32. /*
  33. Y<int, double, char, double> y3;
  34. X<char, double>& x3 = y3;
  35. x3.foo (1.2); */
  36. return 0;
  37. }

C++STL - 模板的其他特性的更多相关文章

  1. 泛型编程、STL的概念、STL模板思想及其六大组件的关系,以及泛型编程(GP)、STL、面向对象编程(OOP)、C++之间的关系

    2013-08-11 10:46:39 介绍STL模板的书,有两本比较经典: 一本是<Generic Programming and the STL>,中文翻译为<泛型编程与STL& ...

  2. DLL中导出STL模板类的问题

    接上一篇. 上一篇的dll在编译过程中一直有一个警告warning C4251: ‘CLASS_TEST::m_structs’ : class ‘std::vector<_Ty>’ ne ...

  3. STL模板_容器概念

    一.STL(Standard Template Library,标准模板库)概述1.容器:基于泛型的数据结构.2.算法:基于泛型的常用算法.3.迭代器:以泛型的方式访问容器中的元素,是泛型的算法可以应 ...

  4. c++ STL模板(一)

    一.sort函数 1.头文件:#include < algorithm>: 2.它使用的排序方法是类似于快排的方法,时间复杂度为n*log2(n): 3.Sort函数有三个参数:(第三个参 ...

  5. STL模板整理 vector

    一.什么是标准模板库(STL)? 1.C++标准模板库与C++标准库的关系 C++标准模板库其实属于C++标准库的一部分,C++标准模板库主要是定义了标准模板的定义与声明,而这些模板主要都是 类模板, ...

  6. C++面试笔记--STL模板与容器

    1.C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作.vec ...

  7. C++ 泛型程序设计与STL模板库(1)---泛型程序设计简介及STL简介与结构

    泛型程序设计的基本概念 编写不依赖于具体数据类型的程序 将算法从特定的数据结构中抽象出来,成为通用的 C++的模板为泛型程序设计奠定了关键的基础 术语:概念 用来界定具备一定功能的数据类型.例如: 将 ...

  8. C++ STL模板

    C++中的STL(Standard Template Library)用起来挺方便的,这里我们来做一下总结. 一.set set是STL中一种标准关联容器 (vector,list,string,de ...

  9. STL模板_智能指针概念

    一.智能指针1.类类型对象,在其内部封装了一个普通指针.当智能指针对象因离开作用域而被析构时,其析构函数被执行,通过其内部封装的普通指针,销毁该指针的目标对象,避免内存泄露.2.为了表现出和普通指针一 ...

随机推荐

  1. C# GZip对字符串压缩和解压

    /// <summary> /// 压缩方法 /// </summary> public static string CompressString(string str) { ...

  2. centos6搭建gitlab

    前言 原来的项目放在公网的gitlab上,处于安全考虑,在内网搭建一套,有图形界面,可以直接从外网git导入进来,使用了一下觉得挺方便,把安装流程记录下来,参考官网:https://gitlab.co ...

  3. Python_Day_05 计数器(counter),有序字典(OrderDict),默认字典(defaultdict),可命名元祖(namedtuple),双向队列(deque),单项队列(deuqe.Queue)

    Counter(计数器) 是一个字典的子类,存储形式同样为字典,其中存储的键为字典的元素,值为元素出现的次数,在使用之前我们需要先导入文件 import collections 初始化一个计数器 im ...

  4. Effective java笔记(四),泛型

    泛型为集合提供了编译时类型检查. 23.不要在代码中使用原生态类型 声明中具有一个或多个类型参数的类或接口统称为泛型.List<E>是一个参数化类,表示元素类型为E的列表.为了提供兼容性, ...

  5. 【非原创】IOS 验证文字是否是中文

    从环信中找到的部分不错的代码,拿出来记录一下 是否是中文 -(BOOL)isChinese{ NSString *match=@"(^[\u4e00-\u9fa5]+$)"; NS ...

  6. Redis修改数据多线程并发—Redis并发锁

    本文版权归博客园和作者本人吴双共同所有 .转载爬虫请注明地址,博客园蜗牛 http://www.cnblogs.com/tdws/p/5712835.html 蜗牛Redis系列文章目录http:// ...

  7. 【C#】New操作符所做的事情

    1.它计算类型以及所有基类型(一直到System.Object,虽然它没有定义自己的实例字段)中定义的所有实例字段需要的字节数.堆上的每个对象都需要一些额外的成员---即“类型对象指针”和“同步块索引 ...

  8. php中的登陆login

    Login <?php require "../include/DBClass.php"; $username=$_POST['UserName']; $password=$ ...

  9. Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室)

    简介       ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端 ...

  10. SQL Server:APPLY表运算符

    SQL Server 2005(含)以上版本,新增了APPLY表运算,为我们日常查询带来了极大的方便. 新增的APPLY表运算符把右表表达式应用到左表表达式中的每一行.它不像JOIN那样先计算那个表表 ...