C++进阶教程之模板3--一些知识的填充(霜之小刀 附视频)

来源:互联网 发布:java中scanner的作用 编辑:程序博客网 时间:2024/04/29 09:01

C++进阶教程之模板3--一些知识的填充(霜之小刀)

QQ:2279557541

Email:lihn1011@163.com

博客地址:http://blog.csdn.net/lihn1987

优酷主页:http://i.youku.com/shuangzhixiaodao

1. typename的另外一种用途

前面的几期内容中,我们知道了typename的一个功能,就是用于声明模板参数中的类型参数。比如

template<typename T>

void func(){

....

}

但是typename还有另外一种用途。用于定义后面跟着的是一个类型。

比如我们定义一个模板.

#include <vector>

template<typename T>

class TestClass

{

private:

T::iterator iter;

};

int main()

{

TestClass<std::vector<int>>a;

system("pause");

return 0;

}

我们想这个模板接受的模板参数都是标准库中的容器,然后在容易中定义一个容器的迭代器(iterator)类型的变量。

但是我们发现程序报错了。为何报错?

他把T::iterator看成了iterator是T的一个静态成员变量。。。。

那么怎样让编译器知道T::iterator是一种类型呢?就是在前面加一个typename告知编译器,后面跟着的是一个类型。

template<typename T>

class TestClass

{

private:

typename T::iterator iter;

};

int main()

{

TestClass<std::vector<int>>a;

system("pause");

return 0;

}

 

目前看到的这种情况是发生在成员变量的定义时,那要是在函数中定义依赖模板参数T的成员类型的变量会发生什么情况呢?

template<typename T>

class TestClass

{

public:

void function()

{

T::iterator pos;

}

private:

typename T::iterator iter;

};

int main()

{

TestClass<std::vector<int>>a;

a.function();

system("pause");

return 0;

}

我所使用的vs2015的编译器并没有报错。但是为了防止其他的一些编译器会报错,我们可以养成一种习惯,就是任何依赖传入模板参数的类型前都加一个typename。

2. 模板的模板参数

听着就有点绕,这一节也会比较绕。

比如我们写一个类,想用来封装一个标准库的容器。

我们期望的使用方式是这样的

MyClass<int, std::vector>

就是分装一个用std::vector<int>来存储数据的类型。

#include <iostream>

#include <string>

#include <vector>

#include <list>

template<typename T>

class MyList:public std::list<T>

{

};

 

template<typename T>

class MyVector :public std::vector<T>

{

};

 

template <typename T1,template<typename TMP>class T2>

class TestClass

{

private:

T2<T1>m_list;

};

int main()

{

TestClass<int,MyVector> a;

system("pause");

return 0;

}

首先代码中先写了两个模板类MyList与MyVector分别继承自std::list与std::vector,这里为什么要写这两个完全没有用单单是继承的模板类呢?

这个问题我们放到后面再讲,这里只需要把MyList当成std::list,把MyVector当成std::vector的功能即可。

然后看我们的TestClass,他的模板参数是这样定义的

template <typename T1,template<typename TMP>class T2>

其中第一个参数容易理解,但是第二个参数怎么理解呢?因为我们传入的不是一个固定的类型,而是一个模板类的名称,所以这里就要先声明一个模板类

template<typename TMP>class T2

这就是我们定义的模板类,其中T2就是我们那个模板类的名称,而TMP其实是完全没有用到的,可以省略因此我们这里可以吧这一行改为

template <typename T1,template<typename>class T2>

这就是模板的模板参数。

 

本来呢说到这其实就已经完了,但是我们看这段代码不觉得很别扭么?

为什么非要用MyList与MyVector???如果我们直接用标准库中的容器会如何?

于是我们把

TestClass<int,MyVector> a;

改为

TestClass<int,std::vector> a;

结果,编译器报错!!!!为何????

于是看一下std::vector的定义为

template<class _Ty,

class _Alloc =allocator<_Ty> >

class vector{

.....此处省略无数代码

}

原来他的模板定义不止有一个参数

于是我们修改下我们的TestClass

然后源代码变为:

#include <iostream>

#include <string>

#include <vector>

#include <list>

 

template <typename T1,template<typename TMP,typename _Alloc =std::allocator<TMP>>class T2>

class TestClass

{

private:

T2<T1>m_list;

};

int main()

{

TestClass<int,std::vector>a;

system("pause");

return 0;

}

这样貌似简介多了,再也没有中间的那个什么MyList,MyVector这种鬼了。但是我们看看模板参数这一行

template <typename T1,template<typename TMP,typename _Alloc =std::allocator<TMP>>class T2>

这个里面的TMP由于后面要用到,没法省略了,但是那个_Alloc貌似没啥用了啊,这个能省么???

尝试一下改为

template <typename T1,template<typename TMP,typename  = std::allocator<TMP>>class T2>

没错!果然是可以省略的!

至此,我们的需求终于实现了,这一节的内容也就讲完了

3. 模板中字符串的推导

首先先看下面这个例子

template<typename T>

void func1(T a,T b)

{

std::cout << typeid(T).name()<< std::endl;

}

 

template<typename T>

void func2(T&a, T&b)

{

std::cout << typeid(T).name()<< std::endl;

}

 

int main()

{

func1("aaa","bbb");

func1("aaa","cccc");

func2("aaa","bbb");

//func2("aaa", "cccc");编译器报错

system("pause");

return 0;

}

输出为:

char const *

char const *

char const [4]

这种输出的原因是,当模板参数传入的类型不是引用时,数组会自动被推导为指针,而第二个模板由于函数模板由于传入的是引用,则使用了原来的数组类型,所以备注释报错的那一行中的”aaa”,”cccc”得类型分别是char const [4]和 char const[5]的类型。

这里多讲一点,字符串常量,也即是我们代码里直接写的”aaa”这种,其实编译器会将其识别为一个char const [4]的数组,而数组里的四个元素分别为字符’a’,’a’,’a’,’a’,’\0’

相关视频请在我的优酷主页中查找。

 

 

 

0 0
原创粉丝点击