函數模板和關鍵字decltype用法的註記

【以下代碼均在VS2015環境下調試】

============================================================================

1.

對於一個函數模板,其返回兩個變量之和

template<typename T>
T f(T& a, T&b)
{
return a + b;
}
另外再次定義一個“顯式專用化”(explicit specialization)作爲函數f的一個例外,其語法格式如下

template<>  type  f  <type>  (argument_list)

其中f之後的<type>是可選的,也就是說可以寫爲一下形式

template<> type  f   (argument_list)

例如返回兩個參數的差,可以有

template<>double f(double&a, double&b)
{
	return a - b;
}
或者

template<>double f<double>(double&a, double&b)
{
	return a - b;
}
特別注意以下是錯誤的

template<>T f<double>(double&a, double&b)
{
	return a - b;
}
不能指定一個明確類型(double)後還使用模板類型T。
接下來在主函數定義int和double兩個類型變量

void main()
{
	using std::cout;
	using std::endl;
	int a = 1, b = 2;
	double c = 2.25, d = 3.25;
	cout << f(a, b) << endl;
	cout << f(c, d) << endl;
}
其輸出結果是,f(a,b)=3,,f(c,d)=-1。可見當兩個參數是int(或者其他不是double的變量都將調用模板),而遇到兩個double變量c、d時將調用顯式專用化函數返回二者之差。

然而,如果返回類型不是空void時候,可以使用auto+decltype(C++11支持)關鍵字來指定返回類型(尾隨返回類型)

template<> <strong><span style="color:#3333ff;">auto</span></strong> f<double>(double&a, double&b)-><strong><span style="color:#3333ff;">decltype</span></strong>(a+b)
{
	return a - b;
}
也就是說它返回a+b的類型。也就是延遲指定返回類型(trailing return type)

template<typename T>
T f(T& a, T&b)
{
	return a + b;
}
template<> auto f<double>(double&a, double&b)->decltype(a+b)
{
	return a - b;
}
但是注意,兩個函數不能同時聲明auto+decltype

template<typename T>
auto f(T& a, T&b)->decltype(a + b)
{
	return a + b;
}
template<> auto f<double>(double&a, double&b)->decltype(a+b)
{
	return a - b;
}
或者

template<typename T>
auto f(T& a, T&b)->decltype(a + b)
{
	return a + b;
}
template<> double f<double>(double&a, double&b)
{
	return a - b;
}
均提示

C2912 顯式專用化;“double f<double>(double &,double &)”不是函數模板的專用化 

【目前我也不知道爲什麼(⊙o⊙)…】


但是考慮到,如果同名的非模板函數和模板函數同時存在的話,則非模板函數會覆蓋(override)模板函數,由此可以

template<typename T>
auto f(T a, T b)->decltype(a + b)
{
	return a + b;
}
auto f(double a, double b)->decltype(a + b)//或auto f(double a, double b)->double
{
	return a - b;
}
此時如果主函數

void main()
{
	using std::cout;
	using std::endl;
	int a = 1, b = 2;
	double c = 2.25, d = 3.25;
	cout << f(a, b) << endl;
	cout << f(c, d) << endl;
}
仍然顯示f(a,b)=3,,f(c,d)=-1。這就說,儘管int可以轉爲double,但是如果兩個類型相同變量均不是double,此時模板函數是最佳匹配。

進一步,如果至少一個是double,那麼就會模板函數就不是最佳匹配,會調用非模板函數

void main()
{
	using std::cout;
	using std::endl;
	int a = 1, b = 2;
	double c = 2.25, d = 3.25;
	cout << f(double(a), b) << endl;
	cout << f(c, d) << endl;
}
則顯示-1,-1。


另外,如果兩個變量類型不同,則會自動轉化爲double調用非模板函數而不是模板函數,因爲模板函數保證兩個參數的類型必須一致,而此處不一致。

void main()
{
	using std::cout;
	using std::endl;
	int a = 1, b = 2;
	double c = 2.25, d = 3.25;
	cout << f(float(a), short(b)) << endl;
	cout << f(c, d) << endl;
}
則顯示-1,-1。

然而模板專用化和非模板函數的一個區別在於:

如果我們有

template<typename T>
T f(T a, T b)//primary template 
{
	return a + b;
}
template<>double f(double a, double b)//專用化
{
	return a - b;
}
那麼若主函數中有

void main()
{
	using std::cout;
	using std::endl;
	int a = 1, b = 2;
	cout << f(float(a), short(b)) << endl;
}
其會提示錯誤

沒有與參數列表匹配的 函數模板 "f" 實例(cout << f(float(a), short(b)) << endl; )

C2782“unknown-type f(T,T)”: 模板 參數“T”不明確(cout << f(float(a), short(b)) << endl; )

由此可見,專用化的形式上仍然和模板函數一致,但是非模板函數則沒有限制。

C++允許:同名的:非模板函數、模板函數、模板專用化函數以及其各自的重載函數同時存在。如果三類型均匹配當前調用,則就優先級而言,非模板函數先於模板專用化函數,模板專用化函數先於模板函數。


2.

模板實例化(instantiaion)和模板專用化(specialization)

模板專用化第一部分已經提及,它相當於,對於該模板,對於某些特別類型的需要特殊算法,則可以聲明一個專用化。然而注意到,一個函數模板只是一個框架,實際上在沒有調用之前它什麼也不是,只用調用時纔會產生相應類型的函數代碼。此時我們成爲模板實例化。其實之前已經接觸到了實例化例子,只不過是隱式實例化

int a=1,b=2;
cout<<f(a,b)<<endl;
也就是說,f(a,b)此處調用了模板來實例化了一個針對int類型的函數代碼。

那麼當然也可以有顯示實例化,聲明如下:

template<typename T>
T f(T a, T b)
{
	return a + b;
}
template int f<int>(int, int);//模板實例化


特別注意和專用化的語法格式區別

template<strong><span style="color:#ff0000;"><></span></strong> int f<int>(int, int);//模板實例化
可見實例化沒有尖括號。
這個模板實例化的代碼只能在全局範圍聲明,不能在代碼塊內聲明。比如如果

void main()
{
	using namespace std;
	template int f<int>(int, int);

則提示錯誤

C2951 模板 聲明只能在全局、命名空間或類範圍內使用

當前範圍內不允許顯式實例化

C2252 只能在命名空間範圍內顯式實例化模板

注意到如果聲明瞭一條實例化語句,則相當於產生了確定的函數代碼。

當然在代碼塊內顯式聲明實例化,則要類似於:

cout << f<int>(a,b)<< endl;
這就聲明瞭一個int類型的函數實例。

接下來如果將其聲明爲double類型的函數實例代碼:

cout << f<double>(a,b)<< endl;

則會調用專用化函數

template<>double f(double a, double b)
{
	return a - b;
}

這是因爲專用化函數仍然屬於模板,而且是專門針對double類型的數據,儘管此處a、b都是int類型變量。注意如果這個時候沒有專用化而是一個同名double型函數:

double f(double a, double b)
{
	return a - b;
}

cout << f<double>(a,b)<< endl;
是不會調用非模板函數,因爲非模板函數不屬於模板,而此處顯然是一個模板實例化聲明。

實例化聲明語句可以有很多條。


3.

選擇哪一個函數?

如果有兩個模板,是重載的函數:

template<typename T>
void f(T a, T b)//@1
{
	cout<<"general"<<endl
}
template<typename T>
void f(T* a, T* b)//@2
{
	cout << "pointer" << endl
}
則如果參數是一個int指針int*,選擇哪一個?是選@1,使得T變成int*,還是@2,使得T變成int?儘管兩個都解釋的同,此時編譯器選擇明確指示的一個,因爲@2的參數列表已經指明瞭它需要兩個T型指針,因此如果傳入兩個int指針,那麼會顯示“pointer”而不是“general”。

注意到,如果僅僅只有

template<typename T>
void f(T a, T b)
{
	cout<<"general"<<endl
}
那麼傳入兩個int'*指針,則能且只能選擇f,把T解釋爲int*。









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