C 11移动语义
C++11 Move Semantics
我一直在试图通过Bjarne Stroustrup的精彩C 书籍在C 11中正确使用Move语义。我遇到了一个问题 - 移动构造函数并没有像我期望的那样被称为。采用以下代码:
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(getTest(1) getTest(2) getTest(3))的结果是一个rvalue(因为它没有名称,因此,没有任何方法在将其分配给变量测试之后使用),因此应使用移动构造函数构建它,而不是需要向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
我相信在这种情况下应该发生的事情 - 这里有很多新的对象创建,但是在更紧密的想法中,这是有道理的,因为每个时都称为操作员 ,必须创建一个临时对象。<<<<<<
有趣的是,如果我在发布模式下编译了修订的代码,则移动构造函数从未被调用,但是在调试模式下,它被称为上述控制台输出。
编辑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操作员 ,不是为操作员的每一个使用创建一个新对象,这意味着操作员的长链将具有更好的性能。
我认为现在正确理解这个lvalue/rvalue/move语义魔术。
getTest(1) + getTest(2) + getTest(3)
的结果与Test::operator+(const Test&)
的返回类型相同。它是Test&
,因此是LVALUE。
operator +
通常是一个非成员过载,它返回临时值:
Test operator + (const Test& a, const Test& b)
或
Test operator + (Test a, const Test& b)
用于实现operator +=
作为成员的奖励积分,并将其用于实现非会员operator+
。
- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- 可以使用移动语义更改或改进此C++代码吗?
- c++在使用指针时移动语义
- 使用移动和复制语义时函数匹配如何工作?
- 移动语义和深层/浅层复制之间有什么关系?
- 了解构造函数在移动、复制、赋值语义中的行为
- std::unique_lock移动语义
- 移动语义和运算符 + 重载
- C++ 移动语义是否在任何情况下都能节省资源?
- 移动语义在这里如何工作?
- 使用移动语义:右值引用作为方法参数
- 在C++中使用移动语义的正确方法是什么?
- 移动语义 c++ 单链表
- C++:使用整数移动语义
- 当变量和参数名称匹配时,移动语义构造失败
- 在 C++11 中移动语义
- 方法冗余移动调用的移动语义
- 视觉理解移动C++标准的语义
- 如何通过move语义移动多个参数