简体   繁体   English

C ++ 11移动语义

[英]C++11 Move Semantics

I've been trying to teach myself the correct use of move semantics in C++11 through Bjarne Stroustrup's wonderful C++ book. 我一直试图通过Bjarne Stroustrup精彩的C ++书籍自学,正确使用C ++ 11中的移动语义。 I've encountered a problem - the move constructor is not being called as I expect it to be. 我遇到了一个问题 - 移动构造函数没有像我预期的那样被调用。 Take the following code: 请使用以下代码:

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);
}

This code will not compile - because I have deleted the default copy constructor. 此代码将无法编译 - 因为我已删除默认的复制构造函数。 Adding the default copy constructor, the console output is as follows: 添加默认的复制构造函数,控制台输出如下:

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

However, changing the main function to the following: 但是,将主要功能更改为以下内容:

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

Produces the desired console output: 生成所需的控制台输出:

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

This confuses me, because as far as I understand it, the result of (getTest(1) + getTest(2) + getTest(3)) is an rvalue (because it has no name, thus, no way to be used after it is assigned to the variable test) so it should be constructed using the move constructor by default, rather than requiring an explicit call to std::move(). 这让我感到困惑,因为据我所知,(getTest(1)+ getTest(2)+ getTest(3))的结果是一个rvalue(因为它没有名称,因此,之后无法使用它)被分配给变量test)所以它应该默认使用move构造函数构造,而不是要求显式调用std :: move()。

Could somebody explain why this behaviour is occurring? 有人可以解释为什么会出现这种情况吗? Have I done something wrong? 我做错了什么吗? Do I just misunderstand the basics of move semantics? 我只是误解了移动语义的基础知识吗?

Thank you. 谢谢。

EDIT 1: 编辑1:

I updated the code to reflect some of the comments below. 我更新了代码以反映下面的一些评论。

Added in class definition: 在类定义中添加:

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; }

Changed main to: 主要改为:

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

This produces console output: 这会产生控制台输出:

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

Which I believe is what should be happening in this case - there is a lot of new object creation going on here, but on closer thought that makes sense, because each time operator+ is called, a temporary object must be created. 我相信在这种情况下应该发生什么 - 这里有很多新的对象创建,但仔细考虑是有道理的,因为每次调用operator +时,都必须创建一个临时对象。

Interestingly, if I compile the revised code in release mode, the move constructor is never called, but in debug mode, it is called as the console output above describes. 有趣的是,如果我在发布模式下编译修改后的代码,则永远不会调用移动构造函数,但在调试模式下,它会像上面描述的控制台输出一样被调用。

EDIT 2: 编辑2:

Refining it even further. 进一步完善它。 Add to class definition: 添加到类定义:

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

Produces the console output: 生成控制台输出:

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

Which is precisely the desired output. 这正是所需的输出。

EDIT 3: 编辑3:

I believe it would be better to do the following. 我相信做以下事情会更好。 Edit in class definition: 在类定义中编辑:

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; }

This produces the console output: 这会产生控制台输出:

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

Which is more descriptive. 哪个更具描述性。 By implementing the rvalue operator+, a new object is not created for every single use of operator+, meaning that long chains of operator+ will have significantly better performance. 通过实现rvalue运算符+,不会为每次使用operator +创建新对象,这意味着operator +的长链将具有明显更好的性能。

I think properly understand this lvalue/rvalue/move semantics magic now. 我认为现在正确理解这个左值/右值/移动语义魔法。

The result of getTest(1) + getTest(2) + getTest(3) has the same type as the return type of Test::operator+(const Test&) . getTest(1) + getTest(2) + getTest(3)的结果与Test::operator+(const Test&)的返回类型具有相同的类型。 It's Test& , and therefore an lvalue. 它是Test& ,因此是左值。

operator + is typically a non-member overload that returns a temporary by value: operator +通常是非成员重载,它返回临时值:

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

or 要么

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

Bonus points for implementing operator += as a member, and using it in the implementation of the non-member operator+ . 实现operator +=作为成员,并在非成员operator+的实现中使用它的加分点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM