右值引用 移动构造函数 移动语义

一篇讲的很好的博客:https://www.jianshu.com/p/d19fc8447eaa

左值 右值:

看能不能对表达式取地址,如果能,则为左值,否则为右值

将亡值和纯右值:

左值 右值:

纯右值:临时变量,.rodata

将亡值:std::move()将一个左值转换为右值, T&&函数返回值

看能不能对表达式取地址,如果能,则为左值,否则为右值。

将亡值和纯右值:

移动构造函数

class D
{
private:
	int val1;
	int val2;
public:
	D(int x= 0, int y=0): val1(x), val2(y){ cout << "D construct" << endl;}
	D(const D& d) { cout << "D copy construct" << endl; }
	D(const D&& d) { cout << "D move construct" << endl; }
};
int main()
{
	D d;
	D d1(move(d));
	return 0;
}

结果:

D construct
D move construct

如果不用move,那么默认的是调用拷贝构造函数

假设有下面的代码

vector<string> vec;
for (int i = 0; i < 1000; i++)
{
    vec.push_back(string("123")); // 调用构造函数和拷贝构造函数
}

改成

vector<string> vec;
for (int i = 0; i < 1000; i++)
    vec.push_back(move(string("123")));

调用的是移动构造函数

3.左值引用 右值引用 常量左值引用

左值引用, 使用 T&, 只能绑定左值
右值引用, 使用 T&&, 只能绑定右值
常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值
已命名的右值引用,编译器会认为是个左值

4.高性能的swap(假设T可移动)

template <typename T>
void swap(T& a, T& b)
{
    T tmp(std::move(a));
    a = std::move(b);
    b = std::move(tmp);
}

5.通用引用

universal references(通用引用)

template<class T>
void fun(T&& x)
{

}

fun(1); // 右值

int x = 1;
fun(x); // 左值

是左值引用还是右值引用却决于它的初始化

universal references仅仅发生在 T&& 下面,任何一点附加条件都会使之失效

实际上是左值的右值引用:

class D
{
private:
	int val1;
	int val2;
public:
	D(int x= 0, int y=0): val1(x), val2(y){}
	void print() { cout << "helloworld" << endl; }
};

D getD()
{
	return D();
}

int main()
{
	D&& d = getD();
	d.print();
	return 0;
}

完美转发:

原参数可能是右值,可能是左值,如果还能继续保持参数的原有特征,那么它就是完美的。
void process(int& i){
    cout << "process(int&):" << i << endl;
}
void process(int&& i){
    cout << "process(int&&):" << i << endl;
}

void myforward(int&& i){
    cout << "myforward(int&&):" << i << endl;
    process(i);
}

int main()
{
    int a = 0;
    process(a); //a被视为左值 process(int&):0
    process(1); //1被视为右值 process(int&&):1
    process(move(a)); //强制将a由左值改为右值 process(int&&):0
    myforward(2);  //右值经过forward函数转交给process函数,却称为了一个左值,
    //原因是该右值有了名字  所以是 process(int&):2
    myforward(move(a));  // 同上,在转发的时候右值变成了左值  process(int&):0
    // forward(a) // 错误用法,右值引用不接受左值
}

利用通用引用和forward可以实现完美转发

void RunCode(int &&m) {
	cout << "rvalue ref" << endl;
}
void RunCode(int &m) {
	cout << "lvalue ref" << endl;
}
void RunCode(const int &&m) {
	cout << "const rvalue ref" << endl;
}
void RunCode(const int &m) {
	cout << "const lvalue ref" << endl;
}

template<class T>
void pf(T&& a)
{
	RunCode(forward<T>(a));
}

int main()
{
	int a = 0;
	int b = 0;
	const int c = 0;
	const int d = 0;

	pf(a); // lvalue ref
	pf(move(b)); // rvalue ref
	pf(c); // const lvalue ref
	pf(move(d)); // const rvalue ref
	return 0;
}

总结:

由两种值类型,左值和右值。
有三种引用类型,左值引用、右值引用和通用引用。
左值引用只能绑定左值,右值引用只能绑定右值,通用引用由初始化时绑定的值的类型确定。
左值和右值是独立于他们的类型的,右值引用可能是左值可能是右值,
如果这个右值引用已经被命名了,他就是左值。
引用折叠规则:所有的右值引用叠加到右值引用上仍然是一个右值引用,
其他引用折叠都为左值引用。当T&&为模板参数时,输入左值,
它将变成左值引用,输入右值则变成具名的右值应用。
移动语义可以减少无谓的内存拷贝,要想实现移动语义,
需要实现移动构造函数和移动赋值函数。
std::move()将一个左值转换成一个右值,强制使用移动拷贝和赋值函数,
这个函数本身并没有对这个左值什么特殊操作。
std::forward()和universal references通用引用共同实现完美转发。
用empalce_back()替换push_back()增加性能。
发布了99 篇原创文章 · 获赞 4 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/m0_37313888/article/details/105499473