2011-09-22 27 views
66

我剛發現自己不完全理解std::move()的邏輯。std :: move()如何將值傳遞到RValues中?

起初,我對它進行了搜索,但似乎只有關於如何使用std::move()的文檔,而不是它的結構如何工作。

我的意思是,我知道模板成員函數是什麼,但是當我看着VS2010中的std::move()定義時,它仍然令人困惑。

std :: move()的定義如下。

template<class _Ty> inline 
typename tr1::_Remove_reference<_Ty>::_Type&& 
    move(_Ty&& _Arg) 
    { // forward _Arg as movable 
     return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); 
    } 

是什麼奇怪的第一對我來說是參數,(_Ty & & _Arg),因爲當我打電話的功能就像你看到下面,

// main() 
Object obj1; 
Object obj2 = std::move(obj1); 

它基本上等於

// std::move() 
_Ty&& _Arg = Obj1; 

但是,正如您已經知道的,您不能直接將LValue鏈接到RValue引用,這使我認爲它應該是這樣的。

_Ty&& _Arg = (Object&&)obj1; 

然而,這是荒謬的,因爲的std ::移動()必須爲所有的值工作。

所以我想完全理解這是如何工作的,我也應該看看這些結構。

template<class _Ty> 
struct _Remove_reference 
{ // remove reference 
    typedef _Ty _Type; 
}; 

template<class _Ty> 
struct _Remove_reference<_Ty&> 
{ // remove reference 
    typedef _Ty _Type; 
}; 

template<class _Ty> 
struct _Remove_reference<_Ty&&> 
{ // remove rvalue reference 
    typedef _Ty _Type; 
}; 

不幸的是,它仍然是混亂,我不明白。

我知道這都是因爲我缺乏關於C++的基本語法技巧。 我想知道這些工作是如何徹底的,我可以在互聯網上獲得的任何文件都會受到歡迎。 (如果你可以解釋這一點,那也會很棒)。

+7

「我知道這都是因爲我缺乏關於C++的基本語法技巧。」那麼你還沒有準備好知道它是如何工作的。所有你需要知道的是如何編寫移動構造函數,以及何時以及如何在你想移動構造時使用'std :: move'。這個過程的細節,C++ 11用來使這個過程起作用的「深層魔力」不是你需要關注的東西。你根本就沒有知識的基礎來理解功能的特殊交互作用,從而使其全部工作。 –

+0

尼科博拉斯說什麼。如果你知道自己缺乏基本技能,那麼這就是你需要首先解決的問題,而不是你無法理解std :: move! –

+11

@NicolBolas恰恰相反,這個問題本身就表明OP在這個陳述中是自掏腰包的。正是OP掌握了基本的語法技巧,使他們甚至可以提出問題。 –

回答

111

我們先從移動功能(我清理了一點點):

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

讓我們先從容易的部分 - 也就是,當函數被調用右值:

Object a = std::move(Object()); 
// Object() is temporary, which is prvalue 

和我們move模板被實例化如下:

// move with [T = Object]: 
remove_reference<Object>::type&& move(Object&& arg) 
{ 
    return static_cast<remove_reference<Object>::type&&>(arg); 
} 

由於remove_reference轉換T&TT&&T,並且Object沒有引用,我們的最終功能是:

Object&& move(Object&& arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

現在,你可能會問:我們甚至需要投?答案是:是的,我們有。原因很簡單;名爲右值的參考被視爲左值(並且由標準禁止從左值到右值引用的隱式轉換)。


這裏的時候,我們調用move與左值會發生什麼:

Object a; // a is lvalue 
Object b = std::move(a); 

和相應的move實例:

// move with [T = Object&] 
remove_reference<Object&>::type&& move(Object& && arg) 
{ 
    return static_cast<remove_reference<Object&>::type&&>(arg); 
} 

再次,remove_reference轉換Object&Object,我們得到:

Object&& move(Object& && arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

現在我們到了棘手的部分:Object& &&甚至意味着什麼,它如何綁定到左值?

爲了允許完美轉發,C++ 11標準提供了參考特殊規則塌陷,其如下:

Object & & = Object & 
Object & && = Object & 
Object && & = Object & 
Object && && = Object && 

正如你可以看到,根據本規則Object& &&實際上意味着Object&,這是普通的左值允許綁定左值的引用。

最終功能是這樣的:

Object&& move(Object& arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

這是不是不像以前的實例與右值 - 他們都投了參數右值引用,然後返回。區別在於第一個實例化只能用於右值,而第二個實例化只能用於左值。


要解釋爲什麼我們需要remove_reference多一點,讓我們試試這個功能

template <typename T> 
T&& wanna_be_move(T&& arg) 
{ 
    return static_cast<T&&>(arg); 
} 

,並用左值初始化它。

// wanna_be_move [with T = Object&] 
Object& && wanna_be_move(Object& && arg) 
{ 
    return static_cast<Object& &&>(arg); 
} 

施加參考倒塌上述規則,你可以看到,我們得到的功能move是不可用的(簡單地說,你用左值調用它,你就會得到左值回)。如果有的話,這個函數是身份函數。

Object& wanna_be_move(Object& arg) 
{ 
    return static_cast<Object&>(arg); 
} 
+2

很好的答案。雖然我理解對於左值,將'T'評估爲'Object'是一個好主意,但我不知道這是真的完成了。在本例中,我希望'T'也可以評估爲'Object',因爲我認爲這是引入包裝器引用和'std :: ref'的原因,或者不是它。 –

+0

是[本維基百科文章](http://en.wikipedia.org/wiki/C%2B%2B11#Wrapper_reference)過時/誤導或有隱藏的區別我還沒有意識到? –

+0

這是一個非常好的和明確的答案,+1。 – Xeo

2

_Ty是一個模板參數,在這種情況下

Object obj1; 
Object obj2 = std::move(obj1); 

_Ty是鍵入 「對象&」

這就是爲什麼_Remove_reference是必要的。

它會更喜歡

typedef Object& ObjectRef; 
Object obj1; 
ObjectRef&& obj1_ref = obj1; 
Object&& obj2 = (Object&&)obj1_ref; 

如果我們不刪除引用會像我們做

Object&& obj2 = (ObjectRef&&)obj1_ref; 

但ObjectRef & &減少到對象&,這我們無法將不綁定obj2。

它減少這種方式的原因是爲了支持完美的轉發。見this paper

+0

這並不能解釋爲什麼'_Remove_reference_'是必要的。例如,如果你有'Object&'作爲一個typedef,並且你引用了它,你仍然會得到'Object&'。爲什麼不能和&&一起工作?這是一個答案,它與完美的轉發有關。 –

+2

是的。答案非常有趣。 &&&簡化爲A&,所以如果我們試圖使用(ObjectRef &&)obj1_ref,我們會得到Object&而不是在這種情況下。 –