参考文章:
左值引用、右值引用、移动语义、完美转发,你知道的不知道的都在这里
[C++特性]对std::move和std::forward的理解
1. std::move
最近在学习C++11特性,然后关于std::move()的一些理解和疑问先记录一下。
欢迎大神在评论区指正!
1.1 需求
- 目的是为了提高性能,减少拷贝次数
1.2 使用场景
- 和
std::shared_ptr
与引用
使用场景有什么区别?
– 这两者传入函数都不会引起对象内存的拷贝,可以在函数内访问对象
–std::move
配合移动构造函数
使用,在使用对象A创建对象B的时候,把对象A的数据权限移交给B,这样子对象B就不用再去拷贝。所以使用std::move要求对象实现了构造函数,否则没有优化效果
– STL的类型实现了移动构造函数,可以放心使用,如果自己写的类要想实现使用std::move达到性能优化的效果,也要实现移动构造函数
1.3 右值和右值引用
- 右值按照概念的就是不能通过地址访问的数据,需要注意的一点就是字符字面量
"abcd"
是左值 - 右值引用类型使用符号
&&
表示,std::move
的作用就是把左值转换为右值引用类型
–&
是左值应用类型?
– 左值和右值只是属性区分? - 个人理解
假设存在类A,类A实现了拷贝构造
和移动构造
。
因为拷贝构造是C++11之前就实现了,现在想实现移动构造函数,要实现构造函数重载需要参数列表不同,参数列表不同的话可以是个数,类型,或者参数顺序,构造函数只能传入一个参数,所以只能是参数类型不同,为了实现移动构造函数的调用,从而加入了右值引用类型
A a(A()); //调用拷贝构造
A b(std::move(A())); //调用移动拷贝构造
1.4 返回值是临时对象,需要使用std::move
编译器有优化,会使用移动构造函数。
所以在其他场景需要显示调用std::move来决定使用拷贝构造还是移动构造,因为编译器不清楚你接下来是否需要继续使用对象,还是对于对于使用将亡值
进行初始化,都是调用移动构造函数?
2 std::forward
2.1 需求
解决引用折叠导致右值引用被隐式转换为左值引用的场景.
- 这里有点晦涩。然后不太懂
class A;
void test(A && a)
{
A tmp = a; //隐式转换???调用的是拷贝构造
}
void main()
{
A a;
test(std::move(a));
}
引用折叠
T&
&
-> T& (对左值引用的左值引用是左值引用)
T&
&&
-> T& (对左值引用的右值引用是左值引用)
T&&
&
->T& (对右值引用的左值引用是左值引用)
T&&
&&
->T&& (对右值引用的右值引用是右值引用
)
2.2 疑问
- 和std::move搭配使用还是一个取代作用?
- 搭配使用
实例代码
#include <iostream>
using namespace std;
class A
{
public:
A() {}
A(const A&){printf("copy\n");}
A(const A&&){printf("move\n");}
};
void test(A&& a)
{
A tmp = a; //copy. 没使用std::forward进行二次转发的时候,实际上是会被隐式的转换,转发成一个左值引用,从而调用不符合期待的构造函数
A xxx = std::forward<A>(a);//move
}
//使用模本也是一样的
template<typename T>
void improve(T&& a)
{
T tmp = a; //copy
T xxx = std::forward<T>(a); //move
}
int main() {
A a;
test(std::move(a));
improve(std::move(a)); //注意:一般move将亡值,这里再move一次是因为A的移动构造没有实现。再次move会导致没有资源可以转移
return 0;
}