C++進階教程之模板3--一些知識的填充(霜之小刀 附視頻)

C++進階教程之模板3--一些知識的填充(霜之小刀)

QQ:2279557541

Email:[email protected]

博客地址: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’

相關視頻請在我的優酷主頁中查找。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章