C++:25---模板的其它用法(template)

一、使用typedef为模板类型取别名

  • 概念:与其他类型一样,我们可以定义一个typedef来引用实例化的类

提示:

由于模板不是一个类型,所以不能用typedef引用一个模板,在typedef时必须给出特定的类型

  • typedef Blob<string> StrBlob;  //正确
  • typedef Blob<T> StrBlob;  //错误

演示案例

template<typename T> class  Blob
{
};

typedef Blob<string> StrBlob;//当为实例化为string类型的Blob类取别名

int main()
{
    //下面两者等价
    Blob<string> a; 
    StrBlob b;
    return 0;
}

二、使用using为模板类型取别名

  • C++11标准允许使用using定义一个类型别名

演示案例

template<typename T> using twin = pair<T, T>; //取别名

twin<string> authors;//相当于pair<string,string>
twin<int> authors;//相当于pair<int,int>

当我们定义一个模板类型别名时,可以固定一个或多个模板参数

  • 使用时,只能指出pair的T类型,不能指定第二个默认固定的类型
template<typename T> using twin = pair<T, unsigned>;

twin<string> authors;//相当于pair<string,unsigned>
twin<int> authors;//相当于pair<int,unsigned>

三、模板参数

模板参数与作用域的关系

  • 模板参数的名称会覆盖其他名称
typedef double A; //为double数据类型取别名

template<typename A, typename B> void func(A a, B b)
{
	A tmp = a; //tmp的类型为模板参数A的类型,而非double
	double B;  //错误定义,重复声明模板参数B
}

模板的声明与定义

  • 与函数一样,模板也有声明和定义
//声明compare函数模板和Blob类模板
template<typename T> int compare();
template<typename T> class Blob;

//定义compare函数模板
template<typename T> int compare()
{

}
//定义Blob类模板
template<typename T> class Blob
{

};

四、模板默认实参

  • 概念:新标准规定,我们可以为函数和类模板提供默认实参。新标准以前只有类模板才可以提供默认实参

函数模板的默认实参

  • 案例:下面有一个默认模板实参less<T>和一个默认参数实参F()
template <typename T,typename F=less<T>>
int compare(const T &v1, const T &v2, F f = F())
{
	if (f(v1, v2)) return -1;
	if (f(v2, v1)) return -1;
	return 0;
}

类模板的默认实参

//类模板中默认为int类型
template <typename T = int>class Numbers {
public:
    Numbers(T v=0):val(v){}
private:
    T val;
};


int main()
{
    Numbers<> a; //使用默认的int类型
    Numbers<double> b; //替换默认的int,使用double
    return 0;
}

五、控制实例化(extern)

  • 一个重要的概念:当模板被使用时才会进行实例化
  • 一个非常低效的场景:当两个或多个独立编译的源文件使用了相同的模板,并提供了相同的模板参数时,则每个文件中都会有该末班的一个实例

显示实例化

  • 在大多数系统中,在多个文件中实例化相同模板的额外开销可能非常严重。在新标准中,我们可以通过显式实例化来避免这种开销
  • 显式实例化格式如下:
    • declaration是一个类或函数声明,其中所有模板参数必须显式给出

  • 例如:
  • 这个还没看懂,以后补上,见C++ Primer P597

六、效率与灵活性

  • 对于模板设计者所面对的设计选择,标准库只能指针类型给出了一个很好的展示
  • shared_ptr与unique_ptr之间的明显不同在于它们所保管的指针的策略:
    • shared_ptr:共享指针所有权
    • unique_ptr:独占指针所有权
  • shared_ptr与unique_ptr之间的另一个差异是它们允许用户重载默认删除器的方式:
    • shared_ptr:为了重载一个shared_ptr的删除器,可以在创建时或者reset指针时传递给它一个可调用对象即可
    • unique_ptr:与shared_ptr相反,unique_ptr的删除器的类型是unique_ptr类型的一部分,用户必须在定义unique_ptr时显式模板实参的形式提供删除器的类型

在运行时绑定删除器(shared_ptr)

  • shared_ptr必须直接访问其删除器,即,删除器必须保存为一个指针或一个封装了指针的类(如function)
  • 因此,shared_ptr不是将删除器直接保存为一个成员,因为删除器的类型直到运行时才知道。所以,在一起shared_ptr的生存期中,我们可以随时改变其删除器的类型(例如在定义时给shared_ptr一种删除器类型,随后使用reset赋予此shared_ptr另一种类型的删除器)
  • 为了考察删除器是如何正确工作的,我们假设shared_ptr将它管理的指针保存在一个成员p中,且删除器是通过一个名为del的成员来访问的。则shared_ptr的析构函数必须包含类似下面的语句:
//伪代码
//del的值只有在运行时才知道:通过一个指针来调用它
del ? del(p) : delete p; //del(p)需要运行时跳转到del的地址

在编译时绑定删除器(unique_ptr)

  • 在unique_ptr中,删除器的类型是类的一部分
  • 因此,unique_ptr有两个模板参数:
    • 一个用来表示它所管理的指针
    • 另一个表示删除器的类型
  • 由于删除器的类型是unique_ptr的一部分,因此删除器成员的类型在编译时就知道了,从而删除器可以直接保存在unique_ptr对象中
  • unique_ptr的析构函数与shared_ptr类似,也是对其保存的指针调用用户提供的删除器或执行delete
//伪代码
//del在编译时绑定;直接调用实例化的删除器
del(p) //无运行时额外开销
  • 总结:
    • 通过在编译时绑定删除器,unique_ptr避免了间接调用删除器的运行时开销
    • 通过在运行时绑定删除器,shared_ptr使用户重载删除器更为方便
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值