模板實參推斷和引用以及move和forward詳解

模板實參推斷和引用以及move和forward詳解

從左值引用函數參數推斷類型

當一個函數的參數是一個左值引用時,我們只能傳遞給它一個左值,實參可以是const類型或非const類型,實參是const,則T被推斷爲const。
例如:

template<typename T> void f1(T&);
f1(i);  //實參爲int,則T推斷爲int
f1(ci); //實參爲const int,T推斷爲const int
f1(5);  //實參爲右值,不能傳入右值,錯誤

如果一個函數參數類型是const T&,正常綁定規則告訴我們可以給它傳遞任何類型實參–一個對象(const或非const)、一個臨時對象或是一個字面常量值。當函數參數本身是const時,T的類型推斷的結果不會是一個const類型,因爲const已經是函數參數類型的一部分了,它不會再是模板參數類型的一部分。

template<typename T> void f2(const T&);
f2(i);  //i是int,則模板參數類型推斷爲int
f2(ci); //i是const int,模板參數同樣被推斷爲int
f2(5);  //一個const &參數可以綁定到右值,則T爲int

從右值引用函數參數推斷類型

當一個函數參數是一個右值引用時,顯然我們可以傳遞一個右值給它。

template<typename T> void f3(T&&);
f3(42);  //實參是一個int型右值,模板參數類型爲int

引用摺疊和右值引用參數

我們同樣可以將一個左值傳給一個參數爲右值引用的模板函數,此時編譯器會推斷模板類型參數爲實參的左值引用類型,例如當我們調用f3(i)時,此時T被推斷爲int &,而非int。
此時就會出現引用的引用這種情況,這種情況下,這些引用會形成“摺疊”,摺疊成左值的引用或者右值的引用。
* X& &、X& &&、X&& &都被摺疊成X&
* 類型X&& &&被摺疊成X&&

f3(i);  //實參是一個左值,模板參數T推斷爲int&,則參數i類型變爲int& &&,由引用摺疊得到爲int&
f3(ci); //實參是一個左值,模板參數T推斷爲const int&

編寫接受右值引用參數的模板參數

template<typename T> void f3(T &&val)
{
  T t = val;
}
  • 當我們對一個右值調用f3時,例如f3(43),T被推斷爲int,此時,局部變量t是int型,是val的一個拷貝。
  • 當我們對一個左值調用f3時,T被推斷爲int &,此時t的類型是int&,t不是val的拷貝,而是val的一個引用。

move

標準庫是這樣定義move的:

template<typename t>
typename remove_reference<T>::type&& move(T&& t)
{
  return static_cast<typename remove_reference<T>::type&&>(t);
}

當調用std::move(string(“bye”))時,實參爲一個右值:

* 推斷出T類型爲string
* remove_reference<string>::typestring
* 將t強制轉換爲string&&並返回。

當調用std::move(s)時,s爲一個左值:

* 推斷出T類型爲string&
* 則t爲string& &&,摺疊成string&
* remove_reference<string&>::typestring
* 轉換t爲string&&,並返回

forward

除了move()語義之外,右值引用還需要解決的一個問題就是完美轉發,轉發問題針對的是模板函數,要求我們保持實參類型將其傳遞給其他函數。
forward原型類似如下:

template<class TYPE>
TYPE&& forward(typename remove_reference<TYPE>::type& arg)
{
  return static_cast<TYPE&&>(arg);
}

通常情況下,我們使用forward傳遞那些定義爲模板類型參數的右值引用的函數參數。通過其返回類型上的引用摺疊,forward可以保持給定實參的左值/右值屬性:

template<typename Type> intermediary(Type &&arg)
{
  finalFcn(std::forward<Type>(arg));
}

當傳入一個右值參數例如string("ss"):
* Type會被推斷爲string,調用forward<string>
* forward的返回類型TYPE&&string&&
* arg爲remove_reference<string>::type&也就是string&類型
* 再將arg強制爲string&&類型返回,保持了arg的右值屬性。

當傳入一個左值參數,例如string時:
* Type推斷爲string&,則arg爲string& &&類型,引用摺疊得到string&類型。此時調用forward<string&>
* forward返回值爲string& &&,摺疊爲string&
* arg爲remove_reference<string&>::type&string&類型
* 將arg轉爲string&返回,保持了arg的左值屬性。
發佈了47 篇原創文章 · 獲贊 32 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章