1. 程式人生 > >《C++ Templates》筆記——1.函式模板

《C++ Templates》筆記——1.函式模板

1.初探函式模板

函式模板是被引數化的函式,代表的是一個函式家族。它們看起來與普通函式很相似,唯一的區別是有些函式元素是未確定的,這些函式將在使用時被引數化。模板主要有函式模板和類模板。

1.1定義模板

下面定義了一個返回兩個值中最大值的函式模板:

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

其中,要比較的兩個值是通過函式引數a和b傳遞給函式模板的,而引數的型別還沒有確定,所以用模板引數T表示。實際上上面尖括號裡面引數列表的關鍵字也可以用class取代typename,同時T也可以用別的識別符號代替,T是慣例而已。

1.2使用模板

下面的程式展示瞭如何使用max()函式模板:

int main()
{
	int a = max(1, 2);
	std::cout << "a = max(1, 2)= " << a << std::endl;
	
	std::string s1 = "chd";
	std::string s2 = "chdayj";
	::max(s1, s2);
	std::cout << "::max(s1, s2)= " << ::max(s1, s2) << std::endl;
	std::cout <<
typeid(a).name()<<std::endl; system("pause"); return 0; }

上面max()被呼叫了兩次,實參每次都不同,一次兩個int,一次兩個std::string。程式結果如下:

a = max(1, 2)= 2
::max(s1, s2)= chdayj
int

::域限定符的使用時為了保證我們呼叫的是全域性空間中的max(),因為標準庫中也有一個std::max()模板。 typeid(a).name()是顯示元素型別的函式。通常不是把模板編譯成一個可以處理任何型別的單一實體,而是對不同的引數型別產生不同的實體。這種

用具體型別代替模板引數的過程叫做例項化,它產生了一個模板的例項

一個模板一般會被編譯兩次,分別發生在:

  1. 例項化之前,檢查程式碼本身是否語法正確。
  2. 例項化期間,檢查模板程式碼中的呼叫是否有效。這裡是引用

2.實參的演繹

上文中的函式模板當兩個實參不一樣時,比如max(4,4.7),就會出錯,因為c++編譯器不知道T的型別是int還是double。有如下三種方法去解決:

    double a = max(1, 2.1);//ERROR 
	//不允許自動型別轉換,每個T都必須正確匹配,解決方法如下:

	//1.對實參進行強制型別轉換。使它們可以互相匹配
	double a1 = max(static_cast<double>(1), 2.1);
	std::cout << "max(static_cast<double>(1), 2.1)= " << a1 << std::endl;

	//2.顯示指定(或者限定)T的型別
	auto a2 = max<double>(1, 2.1);
	std::cout << "max<double>(1, 2.1)= " << a2 << std::endl;

	//3.指定兩個引數可以具有不同的型別
	double a3 = <int, double>max(1, 2.1);

3.模板引數

#include <iostream>
/*
函式模板有兩種型別的引數:
1.模板引數: T1, T2
2.呼叫引數: a, b
*/
//缺點: 1.函式的值取決於呼叫實參的順序  2.不能通過引用返回結果
template<typename T1, typename T2>
T1 const& max(T1 const& a, T2 const& b)
{
	return a < b ? b : a;
}

template<typename T1, typename T2, typename RT>
RT const& RT_max(T1 const& a, T2 const& b)
{
	return a < b ? b : a;
}

template<typename RT, typename T1, typename T2>
RT  RTmax(T1 const& a, T2 const& b)
{
	return a < b ? b : a;
}

int main()
{
	max(3.2, 2);
	std::cout << "max(3.2, 2)= " << max(3.2, 2) << std::endl;

	max(2, 3.2);
	std::cout << "max(2, 3.2)= " << max(2, 3.2) << std::endl;

	//RT_max(2, 3.2);
	RT_max<int, double, double>(2, 3.2);
	std::cout << "RT_max<int, double, double>(2, 3.2) = "<< RT_max<int,double,double>(2,3.2)<< std::endl;

	RTmax<double>(2, 3.2);
	std::cout << "RTmax<double>(2, 3.2)= " << RTmax<double>(2, 3.2) << std::endl;

	system("pause");
	return 0;
}

很明顯,前兩個模板函式的缺點:

1.函式的值取決於呼叫實參的順序,2和3.2的最大值可以是3,也可以是3.2;

2.不能通過引用返回結果,例子的返回型別必須是T1,而不能是T1 const&。

當模板引數和呼叫引數沒有發生關聯,或者不能用呼叫引數來決定模板引數的時候,就必須顯式指定模板實參。所以在第三個模板函式中,需要引入第三個模板實參型別來定義函式模板的返回型別。然而,模板實參演繹並不適合返回型別,因為RT不會出現在函式呼叫引數型別裡面,所以沒法演繹。所以只能顯式指定模板實參列表。

目前為止,上述例子都是顯式指定所以模板實參,或者不顯示指定任何模板實參。還有一種情況,就是隻顯式指定第一個實參,讓演繹過程推匯出其餘實參。通常而言,必須指定最後一個不能被隱式演繹的模板實參之前的所以實參型別

4.過載函式模板

和普通函式一樣,函式模板也可以被過載,即相同的函式名稱可以具有不同的函式定義。下面的程式敘述瞭如何過載一個函式模板:

#include <iostream>

//1.求兩個int值的最大值
int const& max(int const& a, int const& b)
{
	return a < b ? b : a;
}

//2.求兩個任意型別值中的最大值
template<typename T>
T const& max(T const& a, T const& b)
{
	return a < b ? b : a;
}

//3.求三個任意型別值中的最大值
template<typename T>
T const& max(T const& a, T const& b, T const& c)
{
	return ::max(::max(a,b),c);
}

int main()
{
	::max(17, 22,96);//呼叫具有3個引數的模板
	std::cout << "::max(17, 22,96)= " << ::max(17, 22, 96) << std::endl;

	::max(7.0, 9.0);//(如果模板可以產生更好匹配的函式,則選用模板)呼叫max<double>(通過實參演繹)
	std::cout << "::max(7.0, 9.0)= " << ::max(7.0, 9.0) << std::endl;

	::max('a', 'b');//呼叫max<char>(通過實參演繹)
	std::cout << "::max('a', 'b')= " << ::max('a', 'b') << std::endl;

	::max(17, 22);//呼叫int過載的非模板函式
	std::cout << "::max(17, 22)= " << ::max(17, 22) << std::endl;

	::max<>(17, 22);//呼叫max<int>(通過實參演繹)
	std::cout << "::max<>(17, 22)= " << ::max<>(17, 22) << std::endl;

	::max<double>(17, 22);//呼叫max<double>(沒有實參演繹)
	std::cout << "::max<double>(17, 22)= " << ::max<double>(17, 22) << std::endl;

	::max('a', 42.7);//(對於不同型別的引數,只允許呼叫非模板函式)呼叫int過載的非模板函式
	std::cout << "::max('a', 42.7)= " << ::max('a', 42.7) << std::endl;

	system("pause");
	return 0;
}

更多請參考:
《C++ Templates》