C++11后的初级模板
创始人
2024-04-30 00:57:59
0

文章目录

  • 一、关键字
    • 1.decltype关键字
    • 2.C++14的auto关键字
  • 二、模板类的具体化
  • 三、模板类与继承
    • 1.模板类继承普通类(常见)。
    • 2.普通类继承模板类的实例化版本。
    • 3.普通类继承模板类。(常见:普通类要变成模板类)
    • 4.模板类继承模板类。
    • 5.模板类继承模板参数给出的基类(不能是模板类)。
  • 四、模板类与函数
  • 五、模板类与友元
  • 六、成员模板类
  • 七、将模板类用作参数

一、关键字

1.decltype关键字

在C++11中,decltype操作符,用于查询表达式的数据类型。

  • decltype分析表达式并得到它的类型,不会计算执行表达式。函数调用也一种表达式,因此不必担心在使用decltype时执行了函数。
语法:decltype(expression) var;

decltype推导规则(按步骤):

1)如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同,包括const等限定符。
2)如果expression是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void *)。
3)如果expression是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是expression的引用。
4)如果上面的条件都不满足,则var的类型与expression的类型相同。
如果需要多次使用decltype,可以结合typedef和using。

2.C++14的auto关键字

int func(int x,double y);
等同:
auto func(int x,double y) -> int;
将返回类型移到了函数声明的后面。
auto是一个占位符(C++11给auto新增的角色), 为函数返回值占了一个位置。
这种语法也可以用于函数定义:
auto func(int x,double y) -> int
{// 函数体。
}

C++14标准对函数返回类型推导规则做了优化,函数的返回值可以用auto,不必尾随返回类型。

  • eg:
#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template 
auto func(T1 x, T2 y) -> decltype(x + y)
{// 其它的代码。decltype(x+y)  tmp = x + y;cout << "tmp=" << tmp << endl;return tmp;
}int main()
{func(3, 5.8);
}

二、模板类的具体化

模板类具体化(特化、特例化)有两种:完全具体化和部分具体化。

  • 具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。

  • 具体化的模板类,成员函数类外实现的代码应该放在源文件中。

  • eg:

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。// 类模板
template
class AA {                 // 类模板。
public:T1 m_x;T2 m_y;AA(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "类模板:构造函数。\n"; }void show() const;
};template
void AA::show() const {    // 成员函数类外实现。cout << "类模板:x = " << m_x << ", y = " << m_y << endl;
}
/// 类模板完全具体化
template<>
class AA {
public:int      m_x;string m_y;AA(const int x, const string y) :m_x(x), m_y(y) { cout << "完全具体化:构造函数。\n"; }void show() const;
};void AA::show() const {    // 成员函数类外实现。cout << "完全具体化:x = " << m_x << ", y = " << m_y << endl;
}
/// 类模板部分具体化
template
class AA {
public:T1       m_x;string m_y;AA(const T1 x, const string y) :m_x(x), m_y(y) { cout << "部分具体化:构造函数。\n"; }void show() const;
};template
void AA::show() const {    // 成员函数类外实现。cout << "部分具体化:x = " << m_x << ", y = " << m_y << endl;
}
/int main()
{// 具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。AA    aa1(8, "我是一只傻傻鸟。");   // 将使用完全具体化的类。AA aa2(8, "我是一只傻傻鸟。");   // 将使用部分具体化的类。AA aa3(8, 999999);                      // 将使用模板类。
}

三、模板类与继承

1.模板类继承普通类(常见)。

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。class AA     // 普通类AA。
{
public:int m_a;AA(int a) :m_a(a) { cout << "调用了AA的构造函数。\n"; }void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl;; }
};template
class BB:public AA      // 模板类BB。
{
public:T1 m_x;T2 m_y;BB(const T1 x, const T2 y,int a) : AA(a) , m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }void func2() const { cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl; }
};int main()
{BB bb(8, "我是一只傻傻鸟。",3);bb.func2();bb.func1();
}

2.普通类继承模板类的实例化版本。

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class BB      // 模板类BB。
{
public:T1 m_x;T2 m_y;BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }void func2() const { cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl; }
};class AA:public BB     // 普通类AA。
{
public:int m_a;AA(int a,int x,string y) : BB(x,y),m_a(a) { cout << "调用了AA的构造函数。\n"; }void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl;; }
};int main()
{AA aa(3,8, "我是一只傻傻鸟。");aa.func1();aa.func2();
}

3.普通类继承模板类。(常见:普通类要变成模板类)

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class BB      // 模板类BB。
{
public:T1 m_x;T2 m_y;BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }void func2() const { cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl; }
};template
class AA:public BB     // 普通类AA变成了模板类,才能继承模板类。
{
public:int m_a;AA(int a, const T1 x, const T2 y) : BB(x,y),m_a(a) { cout << "调用了AA的构造函数。\n"; }void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl;; }
};int main()
{AA aa(3,8, "我是一只傻傻鸟。");aa.func1();aa.func2();
}

4.模板类继承模板类。

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class BB      // 模板类BB。
{
public:T1 m_x;T2 m_y;BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }void func2() const { cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl; }
};template
class AA:public BB     // 普通类AA变成了模板类,才能继承模板类。
{
public:int m_a;AA(int a, const T1 x, const T2 y) : BB(x,y),m_a(a) { cout << "调用了AA的构造函数。\n"; }void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl;; }
};template
class CC :public BB   // 模板类继承模板类。
{
public:T m_a;CC(const T a, const T1 x, const T2 y) : BB(x, y), m_a(a) { cout << "调用了CC的构造函数。\n"; }void func3() { cout << "调用了func3()函数:m_a=" << m_a << endl;; }
};int main()
{CC cc(3,8, "我是一只傻傻鸟。");cc.func3();cc.func2();
}

5.模板类继承模板参数给出的基类(不能是模板类)。

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。class AA {
public:AA()         { cout << "调用了AA的构造函数AA()。\n"; }AA(int a) { cout << "调用了AA的构造函数AA(int a)。\n"; }
};class BB {
public:BB()         { cout << "调用了BB的构造函数BB()。\n"; }BB(int a) { cout << "调用了BB的构造函数BB(int a)。\n"; }
};class CC {
public:CC()         { cout << "调用了CC的构造函数CC()。\n"; }CC(int a) { cout << "调用了CC的构造函数CC(int a)。\n"; }
};template
class DD {
public:DD()         { cout << "调用了DD的构造函数DD()。\n"; }DD(int a) { cout << "调用了DD的构造函数DD(int a)。\n"; }
};template
class EE : public T {          // 模板类继承模板参数给出的基类。
public:EE() :T()           { cout << "调用了EE的构造函数EE()。\n"; }EE(int a) :T(a) { cout << "调用了EE的构造函数EE(int a)。\n"; }
};int main()
{EE ea1;                 // AA作为基类。EE eb1;                 // BB作为基类。EE ec1;                 // CC作为基类。EE> ed1;      // EE作为基类。// EE
ed1; // DD作为基类,错误。 }

四、模板类与函数

模板类可以用于函数的参数和返回值,有三种形式:

1)普通函数,参数和返回值是模板类的实例化版本。
2)函数模板,参数和返回值是某种的模板类。
3)函数模板,参数和返回值是任意类型(支持普通类和模板类和其它类型)。

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class AA    // 模板类AA。
{
public:T1 m_x;T2 m_y;AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }void show() const { cout << "show()  x = " << m_x << ", y = " << m_y << endl; }
};// 采用普通函数,参数和返回值是模板类AA的实例化版本。
AA func(AA& aa)
{aa.show();cout << "调用了func(AA &aa)函数。\n";return aa;
}// 函数模板,参数和返回值是的模板类AA。(函数模板的不规范做法)
template 
AA func(AA& aa)
{aa.show();cout << "调用了func(AA &aa)函数。\n";return aa;
}// 函数模板,参数和返回值是任意类型。(函数模板的规范做法)
template 
T func(T &aa)
{aa.show();cout << "调用了func(AA &aa)函数。\n";return aa;
}int main()
{AA aa(3, "我是一只傻傻鸟。");func(aa);
}

五、模板类与友元

模板类的友元函数有三类:

1)非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数。
2)约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
3)非约束模板友元:模板类实例化时,如果实例化了n个类,也会实例化n个友元函数,每个实例化的类都拥有n个友元函数。

1)非模板友元示例:

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class AA    
{T1 m_x;T2 m_y;
public:AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }// 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。//不能重载friend void show(const AA& a){cout << "x = " << a.m_x << ", y = " << a.m_y << endl;}/* friend void show(const AA& a);friend void show(const AA& a);*/
};//void show(const AA& a)
//{
//    cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
//}
//
//void show(const AA& a)
//{
//    cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
//}int main()
{AA a(88, "我是一只傻傻鸟。");show(a);AA b(88, "我是一只傻傻鸟。");show(b);
}

2)约束模板友元示例:

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。// 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
template 
void show(T& a);                                                 // 第一步:在模板类的定义前面,声明友元函数模板。template
class AA    // 模板类AA。
{friend void show<>(AA& a);          // 第二步:在模板类中,再次声明友元函数模板。T1 m_x;T2 m_y;public:AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};template
class BB    // 模板类BB。
{friend void show<>(BB& a);          // 第二步:在模板类中,再次声明友元函数模板。T1 m_x;T2 m_y;public:BB(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};template                                  // 第三步:友元函数模板的定义。
void show(T& a)
{cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}template <>                                                    // 第三步:具体化版本。
void show(AA& a)
{cout << "具体AA:x = " << a.m_x << ", y = " << a.m_y << endl;
}template <>                                                    // 第三步:具体化版本。
void show(BB& a)
{cout << "具体BB:x = " << a.m_x << ", y = " << a.m_y << endl;
}int main()
{AA a1(88, "我是一只傻傻鸟。");show(a1);         // 将使用具体化的版本。AA a2(88, "我是一只傻傻鸟。");show(a2);        // 将使用通用的版本。BB b1(88, "我是一只傻傻鸟。");show(b1);         // 将使用具体化的版本。BB b2(88, "我是一只傻傻鸟。");show(b2);        // 将使用通用的版本。
}

3)非约束模板友元

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。// 非类模板约束的友元函数,实例化后,每个函数都是每个每个类的友元。
template
class AA    
{template  friend void show(T& a);     // 把函数模板设置为友元。T1 m_x;T2 m_y;
public:AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};template  void show(T& a)                     // 通用的函数模板。
{cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}template <>void show(AA& a)                 // 函数模板的具体版本。
{cout << "具体:x = " << a.m_x << ", y = " << a.m_y << endl;
}int main()
{AA a(88, "我是一只傻傻鸟。");show(a);         // 将使用具体化的版本。AA b(88, "我是一只傻傻鸟。");show(b);        // 将使用通用的版本。
}

六、成员模板类

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template
class AA              // 类模板AA。
{
public:T1 m_x;T2 m_y;AA(const T1 x, const T2 y) : m_x(x), m_y(y) {}void show() { cout << "m_x=" << m_x << ",m_y=" << m_y << endl; }templateclass BB{public:T m_a;T1 m_b;BB() {}void show();};BB m_bb;templatevoid show(T tt);
};template
template
void AA::BB::show() { cout << "m_a=" << m_a << ",m_b=" << m_b << endl; 
}template
template
void AA::show(T tt) {cout << "tt=" << tt << endl;cout << "m_x=" << m_x << ",m_y=" << m_y << endl;m_bb.show();
}int main()
{AA a(88, "我是一只傻傻鸟。");a.show();a.m_bb.m_a = "我有一只小小鸟。";a.m_bb.show();a.show("你是一只什么鸟?");
}

七、将模板类用作参数

#include          // 包含头文件。
using namespace std;        // 指定缺省的命名空间。template 
class LinkList             // 链表类模板。
{
public:T*   m_head;          // 链表头结点。int  m_len = len;   // 表长。void insert()     { cout << "向链表中插入了一条记录。\n"; }void ddelete()  { cout << "向链表中删除了一条记录。\n"; }void update()   { cout << "向链表中更新了一条记录。\n"; }
};template 
class Array                // 数组类模板。
{
public:T*   m_data;          // 数组指针。int  m_len = len;   // 表长。void insert()     { cout << "向数组中插入了一条记录。\n"; }void ddelete()  { cout << "向数组中删除了一条记录。\n"; }void update()   { cout << "向数组中更新了一条记录。\n"; }
};// 线性表模板类:tabletype-线性表类型,datatype-线性表的数据类型。
templateclass tabletype, class datatype, int len>
class LinearList
{
public:tabletype m_table;     // 创建线性表对象。void insert()    { m_table.insert(); }         // 线性表插入操作。void ddelete() { m_table.ddelete(); }      // 线性表删除操作。void update()  { m_table.update(); }      // 线性表更新操作。void oper()     // 按业务要求操作线性表。{cout << "len=" << m_table.m_len << endl;m_table.insert();m_table.update();}
};int main()
{// 创建线性表对象,容器类型为链表,链表的数据类型为int,表长为20。LinearList  a;   a.insert();   a.ddelete();   a.update();// 创建线性表对象,容器类型为数组,数组的数据类型为string,表长为20。LinearList  b;b.insert();   b.ddelete();   b.update();
}
  • ref:链接

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...