天天看點

《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》

繼續閱讀