類型推導:函數模板與auto


1.從函數模板談起

函數模板的類型推導機制是在c++98時代就有的,auto的類型推導機制與其基本一致,所以先理解函數模板類型推導。

函數模板可以用如下代碼框架表示:

#template<typename T>
void f(PT param);

f(expr);

PT與T的不同之處在於PT相對於T可能有一些飾詞(adornments),如const 和引用&。

對於模板類型T的推導是PT和expr共同作用的結果。下面分幾種情況討論類型推導的原則:

1)PT是一般的引用或指針

原則:

  • expr是一個引用(指針)類型,忽略其引用(指針)部分;
  • expr其他部分去匹配PT得到T的類型。 (匹配原則就是如果PT有const則expr的const忽略,PT無const,expr有,則添加)

代碼示例如下:

//PT是指針或引用類型,expr的引用或指針被忽略 
//PT是T& 
#template<typename T>
void f(T& param);
int x = 11;
const int cx = x;
const int& rx = x;
f(x);  //PT是int&, T是int 
f(cx); //PT是const int&, T是const int 
f(rx); //PT是const int&, T是const int 


//PT是const T&,expr中的const被忽略 
#template<typename T>
void f(const T& param);
int x = 11;
const int cx = x;
const int& rx = x;
f(x);  //PT是int&, T是int 
f(cx); //PT是const int&, T是int 
f(rx); //PT是const int&, T是int 

//PT是T*, 
#template<typename T>
void f(T* param);
int x = 11;
const int* px = &x;
f(&x);  //PT是int*, T是int 
f(rx); //PT是const int*, T是const int

結果總結如下表格:

 

 

 

 

 

 

 

 

 

 

 

 

2) PT是萬能引用(universal reference, ie &&)                              

注:萬能引用既不是左值引用也不是右值引用,而是遇左則左,遇右則右。

原則:

  • 如果expr是左值引用,則PT和T均被推導為左值引用(這是所有情況中T被推導為引用的唯一情況);
  • 如果expr是右值引用,則與1)情況類似,即引用被忽略,其余部分去匹配PT。

代碼示例如下:

//PT是T&&,左值推導為左值引用,右值引用忽略 
#template<typename T>
void f(T&& param);
int x = 11;
const int cx = x;
const int& rx = x;
f(x);  //x是左值, PT是int&, T是int& 
f(cx); //cx是左值, PT是const int&, T是const int& 
f(rx); //rx是左值, PT是const int&, T是const int&
f(11); //11是右值, PT是int&&, T是int 

結果總結如下表格:

 

3)PT既不是指針,也不是引用

當PT既不是指針,又不是引用(也就是只有T)時,用傳值傳參的方式考慮問題,可以想清楚推導原則。

所謂傳值傳參(pass by value)意味着拷貝一份新的獨立的對象,但內容與原內容相同。

所以有如下原則和解釋:

  • expr如果是引用,則忽略引用部分;
  • expr的const部分,volatile部分也被忽略。(傳值傳參拷貝一份新的,原來是const,不代表新拷貝的就是const);
  • expr存在指向常量的指針(ie const int*)應保留。 (指針拷貝了一份新的,但是他指向的內容仍然是原來的內容,應保證那塊內容不可變);

代碼示例如下:

//PT是T 
#template<typename T>
void f(T param);
int x = 11;
const int cx = x;
const int& rx = x;
const char* ptr1 = "C++11";
const char* ptr2 const = "c++14";
f(x);  //PT是int&, T是int 
f(cx); //PT是int, T是int 
f(rx); //PT是int, T是int 
f(ptr1);//PT是const char*, T是const char* 
f(ptr2);//PT是const char*,T是const char*,指向內容保留不可更改,指針本身const被移除。

結果總結如下表格:

補充1)關於數組參數的推導

數組參數在c++中可以退化為指針進行傳參,所以補充對數組參數的類型推導。

 原則:

  • 當PT為傳值類型(就是T)時,推導為退化的指針類型;
  • 當PT為引用類型(T&),推導為帶有數組大小的明確的數組類型(如const char[13]);

代碼示例如下:

//數組參數 
const char name[] = "C++11"; 
#template<typename T>
void f(T param);
f(name); //PT為 const char*,T被推導為const char*  

#template<typename T>
void f(T& param);
f(name); //PT為const char (&)[6], T被推導為const char[6]

 

補充2) 關於函數參數的推導

函數在c++中也可以退化為指針,所以函數參數的推導和數組基本一致。

直接看代碼示例:

void someFunc(int, double); //類型void(int, double) 
#template<Typename T>
void f1(T param); 
f1(someFunc); //PT 為void(*) (int, double), T推導為 void(*) (int, double); 
#template<Typename T>
void f2(T& param);
f2(SomeFunc); //PT為void(&)(int, double), T推導為 void(int, double); 

總結如下表格:

至此,關於函數模板的推導過程總結完畢。

 

2. 函數模板到auto

auto的類型推導機制與函數模板類型推導機制幾乎完全一樣,除了一個小的注意點。

回顧函數模板類型推導的代碼框架:

#template<typename T>
void f(PT param);

f(expr);

推導出T,需要PT與expr的共同作用。

在帶有auto的表達式中,auto代表的就是T,也就是最后要推導出的結果;等號右邊代表expr;而auto與其飾詞一起,表示的是PT

注意此時最后更關心的是PT的類型,也就是式中待推導變量的類型。如 :

int x = 11const auto& rx = x;  //auto代表T, const auto&代表PT, x代表expr 

所以第一部分中討論的函數模板類型推導原則完全適用於auto的類型推導。

給出代碼示例如下:

auto x = 11; //類型3, auto推導為int
const auto cx = x; // 類型3, auto推導為int, cx類型為int 
const auto& rx = x; //類型1, auto推導為int, rx類型為int& 
auto&& uref1 = x; //左值,auto推導為int&, uref1類型為int& 
auto&& uref2 = cx; //左值,auto推導為const int&, uref2類型為const int& 
auto&& uref3 = 11; //右值,auto推導為int, uref3類型 int&& 
const char name[] = "C++11" ;
auto arr1 = name; // auto推導為const char*, arr1類型const char* 
auto& arr2 = name; //auto推導為const char[6], arr2類型為const char(&)[6] 
void someFunc(int, double);
auto func1 = someFunc(); //auto推導為void(*)(int, double), func1類型為void(*)(int, double)
auto& func2 = someFunc();//auto推導為void(int, double),func2類型為void(&)(int, double) 

注:

1). 上述原則都是與函數模板推導一致的原則。一點不同在於函數模板沒有對於{}的推導,這是auto獨有的。

使用{}初始化,會被推導為std:initializer_list的相關類型。如:

auto x3 = {27}; //推導為std::initializer_list<int>; 

 

2). 在函數返回類型和lambda表達式參數中使用auto,使用的是模板參數類型推導,而不是auto的類型推導;

也就是在此情況下,推導{}會報錯。如:

auto createList() { //error,模板參數類型推導不能處理{} 
    return {1,2,3};
} 

至此,關於auto類型推導總結完畢。

 

參考文獻:

1. Effective Modern C++, Scott Meyers, http://shop.oreilly.com/product/0636920033707.do

2. C++ 11/14最佳實踐, 高博, http://boolan.com/camp/4

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM