文章

刷知乎引出的这篇博客:左值和右值

前言

晚上在电梯里刷知乎的时候,刷到move,于是便好奇多搜索点相关知识,其中左值和右值可算看懂了点了,于是趁着还没睡觉总结一波

内容

左值和右值网上很多通俗的说法是,左边的是左值,右边是右值,比如

1
int a = 5;

a是左值,5是右值,这也是对的,但是呢,他不完全对

1
2
int a = 10;
10 = a;

你会发现第二行10=a这就会报错

再比如

1
2
3
4
5
6
7
8
int func()
{
	return 10;
}

a = 10; // yes
a = func(); // yes
func() = a;  // error

再比如

1
2
3
4
5
6
7
8
9
int& func()
{
	static int test = 10;
	return test;
}

a = 10; // yes
a = func(); // yes
func() = a;  // func()是左值

你看懂了吗,只有它返回的是一个地址才能是左值,而之前直接return 10;这个10在内存中没有任何作用

我们再看

1
2
3
4
5
6
7
8
9
10
11
12
13
void func(std::string& name)
{
	std::cout << name << std::endl;
}

int main()
{
	std::string firstName = "Neil";
	std::string secondName = "Zhu";
	std::string name = firstName + secondName;
	func(name);
	func(firstName + secondName);//编译器提醒这不是一个左值
}

此时编译器提醒func(firstName + secondName);不是一个左值,因为虽然firstName和secondName是左值,但是他们两个临时形成的新字符串firstName + secondName并不是一个有地址的东西,属于临时中间产生的而已,所以报错

但把代码改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
void func(const std::string& name)
{
	std::cout << name << std::endl;
}

int main()
{
	std::string firstName = "Neil";
	std::string secondName = "Zhu";
	std::string name = firstName + secondName;
	func(name);
	func(firstName + secondName);
}

我们发现就是在func里面用了const,这就可以了,虽然参数还是左值引用,但是新字符串firstName + secondName这个临时右值也可以传进去

这就是你能看到为什么C++中有时候会有常量引用,因为它兼容临时的右值和实际存在的左值

再看一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void func(std::string&& name)
{
	std::cout << name << std::endl;
}

int main()
{
	std::string firstName = "Neil";
	std::string secondName = "Zhu";
	std::string name = firstName + secondName;
	func(name); // error
	func(firstName + secondName);
}

这时候func(name);会报错,因为参数是个右值引用,但是name是左值,所以没办法传进去

总结下就是左值引用在const时候可以绑定临时的右值和左值 但是右值引用只能绑定右值

这时候我们整合下代码,重载两个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void func(const std::string& name)
{
	std::cout << name << std::endl;
}

void func(std::string&& name)
{
	std::cout << name << std::endl;
}

int main()
{
	std::string firstName = "Neil";
	std::string secondName = "Zhu";
	std::string name = firstName + secondName;
	func(name);
	func(firstName + secondName);
}

你会发现代码都正确,现在即使左值引用加了const,临时右值也可以穿进去,但其实firstName + secondName走的还是右值引用自己的函数

下次再谈谈移动语义,看知乎的回答,move的作用是转移所有权,比如vector里面存了一些内容,之前直接复制的话,就很浪费空间,因为要一个个把内容复制过去,现在可以用move,直接把vector1的内容地址直接告诉vector2,让vector的指针直接指向这片空间的地址,但要注意的是之前vector1指针就空了

本文由作者按照 CC BY 4.0 进行授权