C++11的feature应该不能算new feature了叭,毕竟这个版本已经年代久远了。来,我们学习一下C++11的右值引用。
什么是左值,什么是右值?
lvalue这个词来自于C语言,指的是可以放在赋值表达式左边的事物——在栈上或堆上分配的命名对象,或者其他对象成员——有明确的内存地址。
rvalue这个词也来源于C语言,指的是可以出现在赋值表达式右侧的对象——例如,文字常量和临时变量。
左值引用
首先我们回顾一下年代更为久远的左值引用。
1 | int var=42; |
左值引用只能被绑定在左值上,而不是右值。 因此左值引用不能这样子写:
1 | int& i=42; // 编译失败 |
不过我们可以钻空子。像这样:
1 | int const& i = 42; |
右值引用
C++11标准介绍了_右值引用_(rvalue reference),这种方式只能绑定右值,不能绑定左值,其通过两个&&
来进行声明:
1 | int&& i=42; // 正确 |
不能绑定左值噢:
1 | int j=42; |
右值引用用途の移动语义
右值通常都是临时的,所以可以随意修改;如果知道函数的某个参数是一个右值,就可以将其看作为一个临时存储或“窃取”内容,也不影响程序的正确性。这就意味着,比起拷贝右值参数的内容,不如移动其内容。动态数组比较大的时候,这样能节省很多内存分配,提供更多的优化空间。
精简版:在传参的时候使用右值引用可以避免在内存中创建重复的变量副本,空间复杂度更低。
举个例子叭,比如老的这种写法就很耗内存:
1 | void process_copy(std::vector<int> const& vec_) |
在以上代码中,一个函数以std::vector<int>
作为一个参数,就需要将其拷贝进来,而不对原始的数据做任何操作。
如果使用右值引用版本的函数来重载这个函数,就能避免在传入右值的时候,函数会进行内部拷贝的过程:
1 | void process_copy(std::vector<int> && vec) |
右值引用用途の 函数模板
如果函数模板参数以右值引用作为一个模板参数,当对应位置提供左值的时候,模板会自动将其类型认定为左值引用;当提供右值的时候,会当做普通数据使用。
我帮你整理一下思路:
1 | if(函数模板参数以右值引用作为一个模板参数){ |
举个栗子,定义一个函数模板:
1 | template<typename T> |
随后传入一个右值,T的类型将被推导为:
1 | foo(42); // foo<int>(42) |
不过,向foo传入左值的时候,T会被推导为一个左值引用:
1 | int i = 42; |
因为函数参数声明为T&&
,所以就是引用的引用,可以视为是原始的引用类型。那么foo()就相当于:
1 | foo<int&>(); // void foo<int&>(int& t); |
这就允许一个函数模板既可以即接受左值,又可以接受右值参数。
小结
我萌先回顾了左值、右值的术语概念和左值引用的语法,然后介绍了右值引用的语法,最后讲了右值引用的两种用途:移动语义和函数模板。
加油啦,你最棒!
Author: 樱花雨