簡體   English   中英

如何推導出std :: move模板參數?

[英]How are std::move template parameters deduced?

假設我們有:

foo(A&& a);

如果你這樣做

A a;
foo(a);

它不會編譯,抱怨不能將左值綁定到A &&。 那很好。

但是,鑒於std :: move的簽名,

template<class T> typename remove_reference<T>::type&& std::move(T&& a);

看起來它需要一個右值引用,就像在foo中一樣,為什么下面的代碼符合?

A a;
std::move(a);

沒有a是左值?

furthur,據說編譯將實例化:

typename remove_reference<A&>::type&& std::move(A& && a);

我不明白為什么不是:

typename remove_reference<A>::type&& std::move(A && a);

在我看來a類型為A ,而不是A&

Nope move沒有采用rvalue-reference,它采用了被社區稱為通用引用的內容。 按類型推導的模板參數根據參考折疊規則進行操作。 這意味着:

  • 如果TK ,那么T&&將只是K&& ;
  • 如果TK& ,則T&&將崩潰為K& ;
  • 如果TK&& ,則T&&將崩潰為T&&

它就像&&&的邏輯 - 和& ,其中&是0, &&是1:

      &    &&
   |-----------|
&  |  &  |  &  |
   |-----|-----|
&& |  &  | &&  |
   |-----------|

這就是rvalues和lvalues的move方式。

例子:

template<typename T>
void f(T&&);

f<int>   // T is int;   plugging int into T makes int&& which is just int&&
f<int&>  // T is int&;  plugging int& into T is int& && which collapse to int&
f<int&&> // T is int&&; plugging int&& into T is int&& && which collapse to int&&

請注意,引用折疊僅發生在模板參數中; 你不能直接輸入int&& &&並期望它編譯。 當然,您沒有像這樣手動指定類型。 這些僅僅是為了顯示哪些引用崩潰了。

所以你真的這樣稱呼它:

int i;

f(i); // T is int&;  int& && collapses to int&
f(4); // T is int&&; int&& && collapses to int&&

參考折疊也是move不返回T&&的原因:如果T是左值引用,則引用將會崩潰,並且move只返回左值引用。 你執行remove_reference來獲得非引用類型,以便&&真正意味着“rvalue-reference”。

您可以在此處了解更多信息: http//isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

句法形式T&& 式演繹的背景下 (包括模板參數推導 ,但例如也宣告作為變量的類型扣除auto表示右值引用,而是什么斯科特邁爾斯稱之為[ 通用參考 ]。 請注意,只有非常特殊的句法形式 T&&表示普遍參考,而其他類似形式則不被視為此類。 例如:

template<typename T> 
void foo(T&& t); <-- T&& is a universal reference

template<typename T> 
void foo(T const&& t); <-- T const&& is NOT a universal reference

template<typename T> 
void foo(S<T>&& t); <-- S<T>&& is NOT a universal reference

template<typename T> 
struct S { void foo(T&& t); }; <-- T&& is NOT a universal reference

通用引用可以綁定到左值和右值。 如果綁定了類型A的左值,則推斷TA&並且由於參考規則折疊A& &&變為A& ),參數的類型將解析為A& (左值引用)。 如果綁定了類型A的右值,則推導出TA並且參數的類型解析為A&& (右值引用)。

[ 注意參考折疊規則可能看起來很復雜,但它們實際上非常簡單:引用Stephan T. Lavavej,“左值引用具有傳染性”,這意味着當形式T&& &T& & T& &&T& &&實例化時,它們總是如此解析為T& - 只有表格T&& &&被解析為T&& ]

這就是當參數是左值T被推斷為T& )時, std::move函數模板將如下實例化的原因:

typename remove_reference<A&>::type&& std::move(A& && a);

當參數是一個rvalueT推斷為A )時,它將被實例化如下

typename remove_reference<A>::type&& std::move(A&& a);

盡管其他人已經說過,但標准只討論了右值引用。

這對於s​​td :: move的工作原理的關鍵是模板參數推導規則中的一個明確的特殊規則:

[...]如果[聲明的函數參數類型]是對cv-unqualified模板參數的右值引用,並且參數是左值,則使用類型“對A的左值引用”代替A來進行類型推導。[ ...]

另一部分是參考折疊的規則,這就是說

如果類型template-parameter [...]表示類型TR是對類型T的引用,則嘗試創建類型“對cv TR的左值引用”會創建類型“對T的左值引用” “,嘗試創建類型”對cv TR的rvalue引用“會創建類型TR。

現在在template<class T> typename remove_reference<T>::type&& std::move(T&& a); 函數參數a匹配上面的規則(“rvalue reference to cv-unqualified template parameter”),因此推導出的類型將是參數類型的左值引用(如果參數是左值)。 在你的情況下,導致T = A&。

將其替換為移動收益率的聲明

remove_reference<A&>::type&& std::move<A&>(A& && a);

使用remove_reference的定義和參考折疊規則(對TR => TR的rvalue引用),使得:

A&& std::move<A&>(A& a);

Scott Meyer的通用參考概念,在其他答案中提出,是一種有用的方法來記住類型推導和參考折疊規則組合的這種令人驚訝的效果:對推導類型的rvalue引用可能最終成為左值引用(如果該類型可以推導為左值參考)。 但是標准中沒有通用的引用 斯科特邁爾斯說:這是謊言 - 但謊言比真相更有幫助......

請注意,std :: forward在此主題上有一個不同的轉折:它使用額外的間接來防止參數推斷(因此必須明確給出類型),但也使用引用折疊將左值作為左值並將右值作為右值轉發。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM