2014-07-16 22 views
1

如果我有一個類C像這樣:移動構造函數的這個實現是否會拋棄移動語義?

class C { 
    std::string s; 

public: 
    C(std::string& s) : s(s) {} 
    C(std::string&& s) : C(s) {} 
}; 

上午我通過調用從string&&構造的string&構造扔掉移動語義?

+0

我不熟悉C++ 11,但在這種情況下,移動構造函數不是簡單的多餘的? – stakx

+0

@stakx移動構造函數基本上只是更快的複製構造函數,但它們只在調用者願意銷燬正在移動的對象時才起作用。所以不,他們做不同的事情。 – anthropomorphic

+5

C在這個代碼之外的默認規定中沒有移動或copy-ctor,所以我不確定你在說什麼。 – WhozCraig

回答

2

是的,你拋出你的移動語義。我可以用最直接的方法來演示如何通過示例。這是我能想出來的最令人激動的事情,所以我希望明確發生了什麼事情。

考慮一下:

#include <iostream> 

struct S 
{ 
    int x; 

    S() :x(1) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    S(const S& s) : x(s.x) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    S(S&& s) : x(std::move(s.x)) { ++x, s.x=0; std::cout << __PRETTY_FUNCTION__ << '\n';} 
    ~S() { std::cout << __PRETTY_FUNCTION__ << ':' << x << '\n';} 
}; 

class C { 
    S s; 

public: 
    C(S& s) : s(s) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    C(S&& s) : C(s) { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
    ~C() { std::cout << __PRETTY_FUNCTION__ << '\n';} 
}; 

int main() 
{ 
    C c{S{}}; 
} 

輸出

S::S() 
S::S(const S &) 
C::C(S &) 
C::C(S &&) 
S::~S():1 
C::~C() 
S::~S():1 

注意的S 實例構造,既不通過移動它們的。被「移動」到預定目標的任何S將具有0打印爲x。沒有。第一個S是來自S{}的最初臨時值;第二個是通過C(C&&)初始值設定項列表調用C(C&)所作的副本。當這段代碼完成後,當我們開始運行析構函數鏈時,存在兩個完全構造的S

現在看這一點,相同的代碼,但採用s構件上移動的語義:

#include <iostream> 

struct S 
{ 
    int x; 

    S() :x(1) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    S(const S& s) : x(s.x) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    S(S&& s) : x(std::move(s.x)) { ++x, s.x=0; std::cout << __PRETTY_FUNCTION__ << '\n';} 
    ~S() { std::cout << __PRETTY_FUNCTION__ << ':' << x << '\n';} 
}; 

class C { 
    S s; 

public: 
    C(S& s) : s(s) { std::cout << __PRETTY_FUNCTION__ << '\n';} 
    C(S&& s) : s(std::move(s)) { std::cout << __PRETTY_FUNCTION__ << '\n'; } 
    ~C() { std::cout << __PRETTY_FUNCTION__ << '\n';} 
}; 

int main() 
{ 
    C c{S{}}; 
} 

輸出的S

S::S() 
S::S(S &&) 
C::C(S &&) 
S::~S():0 
C::~C() 
S::~S():2 

兩個實例仍然構造,但後者是通過移動建設,吮吸第一個這樣做。當我們開始銷燬所有這些東西時,只有一個S實例仍然有效;另一個則通過撤離而死亡。

長話短說,如果你的目標正在移動,你就開始射擊。我希望這有幫助,但我完全願意拋它,如果它不。

+0

對於那些對於__PRETTY_FUNCTION__是什麼感到困惑的人來說,就像我一樣,[這裏](https://gcc.gnu.org/onlinedocs/gcc/Function-Names。 HTML)是一些文檔。 – anthropomorphic

+0

@anthropomorphic它是*令人難以置信的*有用的模板,順便說一句。 – WhozCraig

+0

我覺得這非常難以遵循,但它是有益的,謝謝。 – anthropomorphic

0

當人們提到「完美轉發」時,這並不是什麼提及。這只是一個移動構造函數調用一個拷貝構造函數(這正是我所假設的你的意思)。這是完美轉發:

class Foo{ 
public: 
    template<typename TBar> Foo(TBar&& bar): m_str(std::forward<TBar>(bar)){} 

private: 
    std::string m_str; 
}; 

這到底是怎麼回事的是,因爲模板參數推導和引用摺疊的規則,TBAR & &是所謂的「通用參考」,這意味着它可能將其類型解析爲TBar,TBar&TBar&&(您也可以在組合中添加constvolatile)。 std::forward就像它的名字所說的那樣,並且TBar解析到的任何東西(保留它的推導類型)都會轉發給字符串的構造函數。

+0

這不是一個有效的動作,因爲,首先,這不是一個動作ctor,是一個完美的轉發控制器,可以直接繞過成員參數初始化您的成員。在這種情況下,編譯器爲Foo生成一個默認的移動ctor。你的答案與Foo移動語義無關 – Manu343726

+1

@ Manu343726:... -1,真的嗎?作者改變了他的頭銜;它最初提到「完美轉發」而不是移動語義。因此,我的帖子關於完美轉發。 – Shokwav

+0

如果作者更改了問題,請相應地更新您的答案。我不知道這個問題是編輯過的,我只看到了一個與移動語義無關的答案。再次,更新答案 – Manu343726