1. 左值&右值
int n = 10;
int m = n;
// 10 = n; // 字面量右值
const char* s = "123abc";
// "abcd" = s; // 字面量右值
m = n + 2;
// n + 2 = m; // 中间结果右值
string str = string(s);
// string(s) = str; // 匿名对象右值
- 右值:只能在
=
右边使用的值(字面量、中间结果、临时对象/匿名对象),无法直接取地址,不能使用左值引用。 - 左值:可以在
=
左边使用的值
2. 左值引用&右值引用
#include <iostream>
using namespace std;
void Print(int n){ cout << n << endl; }
void Print2(int& n){ cout << n << endl; }
void Print3(const int& n){ cout << n << endl; }
// 右值引用
void Print4(int&& n){ cout << n << endl; }
int main() {
// int& f = 10; // 字面量不能初始化左值引用
int m = n;
Print(m); // int n = m;
Print(10);// int n = 10;
Print2(m); // int& n = m;
// Print2(10);// int& n = 10; // 右值不能初始化左值引用
Print3(m); // const int& n = m;
Print3(10);// const int& n = 10; // const左值引用可以初始化左值和右值
// Print4(m); // int&& n = m; // 左值不能初始化右值引用
Print4(10);// int&& n = 10;
}
- 左值引用只能用左值初始化
- 右值引用只能用右值初始化
-
const
左值引用可以初始化左值和右值。
右值引用是左值还是右值?
3. 移动构造函数&移动赋值运算符重载
3.1 不可复制对象的移动
#include <iostream>
using namespace std;
// 不可复制对象的移动
class Uncopy{
public:
Uncopy() = default;
~Uncopy() = default;
Uncopy(const Uncopy&) = delete;
Uncopy& operator=(const Uncopy&) = delete;
Uncopy(Uncopy&&){ cout << "rvalue copy constructor" << endl;}
Uncopy& operator=(Uncopy&&) {cout << "rvalue assign " << endl; return *this;}
};
void Param(Uncopy w){}
Uncopy Return(){
Uncopy v;
return move(v);
// return Uncopy(); // 匿名对象
}
int main(){
Uncopy u;
// Uncopy w = u; // 拷贝构造
Uncopy w = Uncopy(); // 移动构造
Uncopy w2 = move(u); // 移动构造
Uncopy v;
// v = u; // 赋值运算符重载
v = Uncopy(); // 移动运算符重载
v = move(u); // 移动运算符重载
// Param(u); // Uncopy w =u;
Param(Uncopy()); // Uncopy w =u;
Param(move(u)); // Uncopy w =u;
Return();
}
不可复制对象,可以使用移动语法做拷贝构造和赋值。
移动的条件:
- 只有右值才能移动,如果左值要移动需要转换成右值(
move()
)。 - 对象的类要实现移动构造函数和移动赋值运算符重载。
3.2 可复制对象的移动
#include <iostream>
#include <vector>
using namespace std;
// 可复制对象使用移动
class Simple{
public:
Simple(){cout<< "constructor" << endl;}
Simple(const Simple&){cout<< "copy constructor" << endl;}
Simple& operator=(const Simple&){cout<< "assign override" << endl; return *this;}
Simple(Simple&&){cout<< "move constructor" << endl;}
Simple& operator=(Simple&&){cout<< "move override" << endl; return *this;}
};
int main(){
Simple simple;
Simple simple2 = move(simple);
simple2 = move(simple);
}
移动的条件:
- 只有右值才能移动,如果左值要移动需要转换成右值(
move()
)。 - 对象的类实现了移动构造函数和移动赋值运算符重载 。否则,执行拷贝操作。
3.3 STL中的移动
#include <iostream>
#include <vector>
using namespace std;
// 可复制对象使用移动
class Simple{
public:
Simple(){cout<< "constructor" << endl;}
Simple(const Simple&){cout<< "copy constructor" << endl;}
Simple& operator=(const Simple&){cout<< "assign override" << endl; return *this;}
Simple(Simple&&){cout<< "move constructor" << endl;}
Simple& operator=(Simple&&){cout<< "move override" << endl; return *this;}
};
int main(){
string s = "abcd";
// string t = s;
string t = move(s);
cout << (void*)s.c_str() << " " << s << endl;
cout << (void*)t.c_str() << " " << t << endl;
vector<int> vec1 = {1,2,3,4,5};
vector<int> vec2;
vec2 = move(vec1);
cout << vec1.size() << "," << vec2.size() << endl;
vector<Simple> vs;
//vs.push_back(simple); // 拷贝构造
//vs.push_back(simple); // 拷贝构造
//vs.push_back(simple); // 拷贝构造
//vs.push_back(simple); // 拷贝构造
vs.emplace_back(move(simple)); // 移动构造
vs.emplace_back(move(simple)); // 移动构造
vs.emplace_back(move(simple)); // 移动构造
}
- C++11
string
和容器都实现移动构造函数和移动赋值运算符重载。 - C++11 STL容器提供了移动操作的成员函数
emplace_*()
。
4. 万能引用&完美转发
#include <iostream>
#include <vector>
using namespace std;
// 可复制对象使用移动
class Simple{
public:
Simple(){cout<< "constructor" << endl;}
Simple(const Simple&){cout<< "copy constructor" << endl;}
Simple& operator=(const Simple&){cout<< "assign override" << endl; return *this;}
Simple(Simple&&){cout<< "move constructor" << endl;}
Simple& operator=(Simple&&){cout<< "move override" << endl; return *this;}
void Test()&{ // 相当于void Test(Simple*& this)
cout << "lvalue Test" << endl;
}
void Test()&&{ // 相当于void Test(Simple*&& this)
cout << "rvalue Test" << endl;
}
};
// void Func(Simple s){}
void Func(Simple& s){
cout << "lvalue" << endl;
}
void Func(Simple&& s){
cout << "rvalue" << endl;
}
// void Func(const Simple& s){}
template<typename T>
void TemplateFunc(T&& param){// 模板参数&&:通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数。
// Func(param);
// 完美转发
Func(forward<T>(param));
forward<T>(param).Test();
}
int main(){
Simple s;
Func(s);
Func(move(s));
s.Test();
Simple().Test();
TemplateFunc(s);// 传入左值 T&& => T& void TemplateFunc(Simple& param)
TemplateFunc(Simple()); // 传入右值 T&& => T&& void TemplateFunc(Simple&& param)
}
- 函数参数中的
&&
表示右值引用,函数模板参数中的&&
表示万能引用。通用引用/万能引用,根据参数左右值类型的不同,生成不同参数类型的函数。 -
forward<T>()
完美转发根据万能引用参数的不同(左值引用还是右值引用),恢复参数的状态(左值引用保持左值,右值引用转成右值),实现函数调用的转发(传入左值,调用左值引用的函数;传入右值,调用右值引用的函数)。