1. 语法
int& left_ref;
int&& right_ref;
2. 引入目的
右值引用引入了两个语义,移动语义(move sementice)和精确传递(perfect forward).
- 右值是即将销毁的对象,在某些场合例如拷贝的时候,我们想直接使用它的资源而重新申请资源。所以我们要区别对待左值和右值,实现函数重载,所以引入。
- 精确传递是函数参数传递过程中保持左值/右值属性和
const/no_const
属性。
3. 移动语义
class MyString{
public:
MyString(){
cout << "default constructor" << endl;
}
MyString(const char* p){
cout << "construct from char*" << endl;
len_ = strlen(p);
init_data_(p);
}
MyString(const MyString& str){
cout << "copy constructor" << endl;
len_ = str.len_;
init_data_(str.data_);
}
MyString& operator=(const MyString& str){
cout << "copy assignment" << endl;
if(this != &str){
len_ = str.len_;
init_data_(str.data_);
}
return *this;
}
~MyString(){
cout << "destructor" << endl;
}
private:
char* data_;
int len_;
void init_data_(const char* p){
data_ = new char[len_ + 1];
memcpy(data_, p, len_);
data_[len_] = '\0';
}
};
- 测试代码1
vector<MyString> vec;
vec.push_back(MyString("hello"));
cout << "main end" << endl;
输出
construct from char*
copy constructor
destructor
main end
destructor
观察得知发生了两次构造,申请了两次内存。
- 测试代码2
MyString str;
str = MyString("hello");
cout << "main end" << endl;
输出
default constructor
construct from char*
copy assignment
destructor
main end
destructor
我们观察到语句str = MyString("hello");
也执行了两次构造,出现了两次资源的申请。
- 添加了移动构造和移动赋值之后。
上面两个测试的输出变为,我们可以看到对于对于临时对象,会选择移动构造,我们可以在移动构造中直接使用临时对象的资源而不重新申请资源。
construct from char*
move copy constructor
destructor
main end
destructor
default constructor
construct from char*
move assignment
destructor
main end
destructor
4. std::move
- 只有在是右值的时候,才会选择移动构造和赋值,但是有时候我们知道一个左值我们以后不会再使用它了,可以把它当成右值来使用。
move
函数提供了这种功能,把左值当成右值使用。 - 另外使用
std::move
可以极大提高swap
的效率。
一般来说swap
的定义如下,其中会有三次资源的重新申请
template<class T>
void swap(T& a, T& b){
T tmp(a); //copy constructor
a = b; //assignment
b = tmp; //assignment
}
而使用move
,可以避免这三次资源的重新申请。
template<class T>
void swap(T& a, T& b){
T tmp(std::move(a)); //move copy constructor
a = std::move(b); //move assignment
b = std::move(tmp); //move assignment
}
5.perferct forwarding
完美转发:参数通过一个函数原封不动地传入第二个个函数,对第二个函数重载的选择,就像直接传入第二个参数一样
void g(int&& data){
cout << "g: right_ref" << endl;
}
void g(int& data){
cout << "g: left_ref" << endl;
}
template<class T>
void template_f(T&& data){
g(std::forward<T>(data));
}
测试代码
int i = 0;
template_f(i);
template_f(0);
输出
g: left_ref
g: right_ref
note
- 这个特性只是在模板函数中才有,在非模板函数中。
void no_template_f(int&& data){
g(data);
}
//int i =0;
//no_template_f(i); // error: an rvalue reference can't bound to a lvalue
no_template_f(1);
输出g: left_ref
,因为在函数中参数data
被当成左值引用。
如果调用g时加上std::forward<int>(data)
,那么输出是g: right_ref
.