1. 程式人生 > 實用技巧 >《C++ Templates》 技巧性基礎知識

《C++ Templates》 技巧性基礎知識

關鍵字typename的相關問題:

首先需要注意的是當T存在內部型別的問題;

例如書上給出的示例:

template <typename T> void printcoll(const T& coll) {
	typename T::const_iterator pos;
	typename T::const_iterator end(coll.end());

	for (pos = coll.begin(); pos != end; ++pos) {
		std::cout << *pos << ' ';
	}
	std::cout << std::endl;
}

如上所示,注意下typename T::XXX的寫法,旨在使用T型別中的型別;

這裡給出的比較典型的例子就是關於STL庫中的問題,例如每一個類中都有一個迭代器型別進行宣告,如果想呼叫類似模板型別T中的型別,必須要在前面加上typename;


關於詳細的類內this以及.template構造詳見第九章;


成員模板的相關問題:

該章節主要想C++ primer一樣,以不同型別的賦值函式為例子,介紹了相關的典型的成員函式模板問題;

其中和C++ primer類似,也介紹了使用STL時自定義內部儲存單元的問題,即之前提到的預設模板形式:

template <typename T, typename CONT = std::vector<T>> class stack {
private:
    CONT elems;
};

但是這也存在一個問題,就是進行類新建的時候需要指定兩個模板引數stack<int,std::vector>s,這明顯是不符合STL一般化描述的,因此可以使用“模板的模板引數”來進行處理;

模板的模板引數:

C++templates在這裡相當不說人話,其實總結下來就是模板套裡面再套一個模板(但是注意,套得模板必須是類模板),並且套入得模板由其他第一層模板引數進行例項化;

所以受上述stack<int,std::vector>s得問題可以得到解決,如果可以通過int初始化std::vector型別,直接可以在使用時候指定一個引數就可以;

宣告形式如下:

k可以看到,在模板引數列表宣告處,第二個是一個新的模板,裡面的模板引數由類結構體內通過T顯示指出;

也就是ELEM直接就是T型別;

這裡注意一點,書上提到,如果不指定預設allocator為ELEM型別,會導致分配空間無法進行;

本質原因是標準庫中std::deque模板中第二個引數是allocator,為預設值,如果不進行指定的話會導致deque得allocator和CONT得型別無法匹配,所以需要顯式指定;

所以書上也提到了至關重要的一點:一定要考慮模板實參得預設模板引數匹配問題! 

而對於後續得使用途中,如果沒有顯式得使用到ELEM等引數,可以直接留空寫typename即可;

程式碼如下所示:

template <typename T, template <typename ELEM, typename = std::allocator<ELEM> >class CONT = std::deque> class Stack {
private:
	CONT<T> elems;
public:
	void push(const T&);
	void pop();
	T top() const;
	bool empty() const {
		return elems.empty();
	}
	template <typename T2, template <typename ELEM2, typename = std::allocator<ELEM2> >class CONT2> Stack<T, CONT>& operator=(const Stack<T2, CONT2>&);
};

template <typename T, template <typename, typename> class CONT> void Stack<T, CONT>::push(const T& elem) {
	elems.push_back(elem);
}

template <typename T, template<typename, typename> class CONT> template <typename T2, template <typename, typename> class CONT2> 
  Stack<T, CONT>& Stack<T, CONT>::operator=(const Stack<T2, CONT2>& op2) { if ((void*)this == (void*)&op2) { return *this; } Stack<T2, CONT2> tmp(op2); elems.clear(); while (!tmp.empty()) { elems.push_front(tmp.top()); tmp.pop(); } return *this; }

其中可以注意一下相關的寫法問題,即不顯式使用的引數沒有必要寫; 

零初始化問題:

零初始化問題主要是針對於模板初始化的相關問題,避免模板初始化的不安全問題;

尤其針對於內建元素,例如T為int等問題;

template <typename T> class myclass {
private:
	T x;
public:
	myclass():x(){}
};

對於模板中的未知型別得元素初始化呼叫x(),可以直接安全初始化,內建元素直接初始化為0;

字串問題:

這裡舉例了字串對比問題,也就是引用和非引用模板傳遞實參問題;

#include<iostream>
#include<stdlib.h>
#include<string>
using namespace std;

template <typename T> inline const T& rmax(const T& a, const T& b) {
	return a < b ? b : a;
}

int main() {
	//string a = "1239";
	//string b = "456";
	cout << rmax("1239", "456") << endl;
	system("pause");
	return 0;
}

 

會出現上述資訊,但是如果直接通過a,b字串來呼叫,並不會出現問題;

本質原因:引用和非引用字串得傳遞型別不同

對於const string& 傳遞的是char[n]陣列而,所以當陣列元素個數不一樣時,就會出現T模板引數不匹配的問題;

對於string形參,傳遞的是const char*指標,所以可以型別匹配;

一般的情況是顯式指定並且使用過載,但是還是建議使用std::string形式,因為有可能出現比較的是指標地址大小的情況;