簡體   English   中英

C ++ 11移動語義

[英]C++11 Move Semantics

我一直試圖通過Bjarne Stroustrup精彩的C ++書籍自學,正確使用C ++ 11中的移動語義。 我遇到了一個問題 - 移動構造函數沒有像我預期的那樣被調用。 請使用以下代碼:

class Test
{
public:
    Test() = delete;
    Test(const Test& other) = delete;
    Test(const int value) : x(value) { std::cout << "x: " << x << " normal constructor" << std::endl; }
    Test(Test&& other) { x = other.x; other.x = 0; std::cout << "x: " << x << " move constructor" << std::endl; }

    Test& operator+(const Test& other) { x += other.x; return *this; }
    Test& operator=(const Test& other) = delete;
    Test& operator=(Test&& other) { x = other.x; other.x = 0; std::cout << "x :" << x << " move assignment" << std::endl; return *this; }

    int x;
};

Test getTest(const int value)
{
    return Test{ value };
}

int main()
{
    Test test = getTest(1) + getTest(2) + getTest(3);
}

此代碼將無法編譯 - 因為我已刪除默認的復制構造函數。 添加默認的復制構造函數,控制台輸出如下:

x: 3 normal constructor
x: 2 normal constructor
x: 1 normal constructor
x: 6 copy constructor

但是,將主要功能更改為以下內容:

int main()
{
    Test test = std::move(getTest(1) + getTest(2) + getTest(3));
}

生成所需的控制台輸出:

x: 3 normal constructor
x: 2 normal constructor
x: 1 normal constructor
x: 6 move constructor

這讓我感到困惑,因為據我所知,(getTest(1)+ getTest(2)+ getTest(3))的結果是一個rvalue(因為它沒有名稱,因此,之后無法使用它)被分配給變量test)所以它應該默認使用move構造函數構造,而不是要求顯式調用std :: move()。

有人可以解釋為什么會出現這種情況嗎? 我做錯了什么嗎? 我只是誤解了移動語義的基礎知識嗎?

謝謝。

編輯1:

我更新了代碼以反映下面的一些評論。

在類定義中添加:

friend Test operator+(const Test& a, const Test& b) { Test temp = Test{ a.x }; temp += b; std::cout << a.x << " + " << b.x << std::endl; return temp; }
Test& operator+=(const Test& other) { x += other.x; return *this; }

主要改為:

int main()
{
    Test test = getTest(1) + getTest(2) + getTest(4) + getTest(8);
}

這會產生控制台輸出:

x: 8 normal constructor
x: 4 normal constructor
x: 2 normal constructor
x: 1 normal constructor
x: 1 normal constructor
1 + 2
x: 3 move constructor
x: 3 normal constructor
3 + 4
x: 7 move constructor
x: 7 normal constructor
7 + 8
x: 15 move constructor

我相信在這種情況下應該發生什么 - 這里有很多新的對象創建,但仔細考慮是有道理的,因為每次調用operator +時,都必須創建一個臨時對象。

有趣的是,如果我在發布模式下編譯修改后的代碼,則永遠不會調用移動構造函數,但在調試模式下,它會像上面描述的控制台輸出一樣被調用。

編輯2:

進一步完善它。 添加到類定義:

friend Test&& operator+(Test&& a, Test&& b) { b.x += a.x; a.x = 0; return std::move(b); }

生成控制台輸出:

x: 8 normal constructor
x: 4 normal constructor
x: 2 normal constructor
x: 1 normal constructor
x: 15 move constructor

這正是所需的輸出。

編輯3:

我相信做以下事情會更好。 在類定義中編輯:

friend Test&& operator+(Test&& a, Test&& b) { b += a; return std::move(b); }
Test& operator+=(const Test& other) { std::cout << x << " += " << other.x << std::endl; x += other.x; return *this; }

這會產生控制台輸出:

x: 8 normal constructor
x: 4 normal constructor
x: 2 normal constructor
x: 1 normal constructor
2 += 1
4 += 3
8 += 7
x: 15 move constructor

哪個更具描述性。 通過實現rvalue運算符+,不會為每次使用operator +創建新對象,這意味着operator +的長鏈將具有明顯更好的性能。

我認為現在正確理解這個左值/右值/移動語義魔法。

getTest(1) + getTest(2) + getTest(3)的結果與Test::operator+(const Test&)的返回類型具有相同的類型。 它是Test& ,因此是左值。

operator +通常是非成員重載,它返回臨時值:

Test operator + (const Test& a, const Test& b)

要么

Test operator + (Test a, const Test& b)

實現operator +=作為成員,並在非成員operator+的實現中使用它的加分點。

暫無
暫無

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

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