C++11 移动语义std::move 和 完美转发std::forward
1、左值和右值
在C++11中可以取地址(有变量名)的就是左值;不能取地址(没有变量名)的就是右值
举例:
int a = fun(b + c)
a是左值,有变量名且可以取地址
fun(b+c)是右值(将亡值)
2、右值引用 和 移动语义(std::move)
引入右值引用的目的就是为了减少内存拷贝,优化程序性能
std::move 就是将一个左值变量强行转换为右值引用,相当于static_cast<T&&>(l_reference)
std::move 并不会真正地移动对象,真正的移动操作是在RD自己实现的移动构造函数中
#include <string.h>
#include <iostream>
class Test
{
public:
Test()
{
buffer = new char[1024];
datasize = 1024;
}
Test(const Test& test)
{
// 调用深拷贝
printf("l copy construct\n");
this->buffer = new char[test.datasize];
memcpy(this->buffer, test.buffer, test.datasize);
this->datasize = test.datasize;
}
Test(Test&& test)
{
printf("r copy construct\n");
// 自己实现真正的移动
this->buffer = test.buffer;
this->datasize = test.datasize;
test.buffer = nullptr;
test.datasize = 0;
}
~Test()
{
if (nullptr != buffer)
{
delete[] buffer;
buffer = nullptr;
}
datasize = 0;
}
private :
char* buffer;
int datasize;
};
int main(int argc, char* argv[])
{
Test test;
// 调用普通的拷贝构造函数
Test test1(test);
// std::move 只是告诉编译器,让其调用移动构造函数,真正的移动需要RD自己实现
Test test2(std::move(test));
return 0;
}
以上代码可以在gcc482以上,使用g++ main.cpp -std=c++11 -g 编译通过
3、完美转发std::forward
当我们用一个变量接收右值引用时,根据C++ 标准的定义,这个变量变成了一个左值。那么他永远不会调用接下来函数的右值版本,这可能在一些情况下造成拷贝。
为了解决这个问题 C++ 11引入了完美转发,支持右值判断的推导,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。
#include <string.h>
#include <iostream>
class Test
{
public:
Test()
{
buffer = new char[1024];
datasize = 1024;
}
Test(const Test& test)
{
// 调用深拷贝
printf("l copy construct\n");
this->buffer = new char[test.datasize];
memcpy(this->buffer, test.buffer, test.datasize);
this->datasize = test.datasize;
}
Test(Test&& test)
{
printf("r copy construct\n");
// 实现真正的移动
this->buffer = test.buffer;
this->datasize = test.datasize;
test.buffer = nullptr;
test.datasize = 0;
}
~Test()
{
if (nullptr != buffer)
{
delete[] buffer;
buffer = nullptr;
}
datasize = 0;
}
private :
char* buffer;
int datasize;
};
int main(int argc, char* argv[])
{
Test test;
// 只是强转为r reference,没做任何其他事
Test&& test1 = std::move(test);
// 由于**test1这个右值引用本身是左值**,此处会调用拷贝构造
Test test2(test1);
// forward支持对test1类型推导,test1为右值引用,会转成右值,调用移动构造函数
Test test3(std::forward<Test>(test1));
return 0;
}
总结
- std::move执行到右值的无条件转换。就其本身而言,它没有move任何东西。
- std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。
- std::move和std::forward在运行期都没有做任何事情。