# 右值引用 移动语意

~~~

右值引用解决了左值引用无法传递临时对象和常引用传递的对象只读的问题. 右值引用允许传递一个可变的临时对象引用.

移动构造使用移动而非赋值语义完成构造过程, 主要用于解决函数返回值时的大量深拷贝开销.

#include

int main(void)

{

int a = 30;

int &b = a;

//int &&c = a; /* error 右值引用不能绑定到左值 */

//int &d = a * 2; /* erro a * 2 是一个右值 */

const int e = a * 2; /* const 的引用可以绑定到一个右值 */

int && f = a * 2; /* 右值引用可以绑定到右值 */

//int && g = f; /* error 不能将一个右值引用绑定到一个变量上, 即使这个变量是右值引用类型也不行 */

int && h = std::move(f); /* ok */

return 0;

}

int r1 = 20;

int &&r2 = std::move(r1);

调用move就意味着承诺: 除了对r1赋值或销毁它以外, 将不能再使用它.

~~~

# 左值引用和右值引用

```

std::move(a) 返回变量右值

std::ref(a) 返回变量引用 主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝

```

# 完美转发

```

std::forward可以保存参数的左值或右值特性

```

# std::move()和std::forward()对比

* std::move执行到右值的无条件转换。就其本身而言,它没有move任何东西。

* std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。

* std::move和std::forward只不过就是执行类型转换的两个函数;std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有做任何事情。它们没有产生需要执行的代码,一byte都没有。

* std::forward()不仅可以保持左值或者右值不变,同时还可以保持const、Lreference、Rreference、validate等属性不变;

```

#include

#include

#include

#include

using namespace std;

struct A

{

A(int&& n)

{

cout << "rvalue overload, n=" << n << endl;

}

A(int& n)

{

cout << "lvalue overload, n=" << n << endl;

}

};

class B

{

public:

template

B(T1 && t1, T2 && t2, T3 && t3) :

a1_(std::forward(t1)),

a2_(std::forward(t2)),

a3_(std::forward(t3))

{

}

private:

A a1_, a2_, a3_;

};

template

std::unique_ptr make_unique1(U&& u)

{

//return std::unique_ptr(new T(std::forward(u)));

return std::unique_ptr(new T(std::move(u)));

}

template

std::unique_ptr make_unique(U&&... u)

{

//return std::unique_ptr(new T(std::forward(u)...));

return std::unique_ptr(new T(std::move(u)...));

}

int main()

{

auto p1 = make_unique1(2);

int i = 10;

auto p2 = make_unique1(i);

int j = 100;

auto p3 = make_unique(i, 2, j);

return 0;

}

```

# C++中的万能引用和完美转发

1. 阅读这篇博文需要了解C++中的左值(lvalue)和右值(rvalue)的概念,详情参见我的另外一篇博文:[C++移动语义及拷贝优化](https://theonegis.github.io/cxx/C-%E7%A7%BB%E5%8A%A8%E8%AF%AD%E4%B9%89%E5%8F%8A%E6%8B%B7%E8%B4%9D%E4%BC%98%E5%8C%96/)

2. 万能引用和完美转发多涉及到模板的使用,如若不是自己写模板,则可不用关心

## 万能引用(Universal Reference)

首先,我们来看一个例子:

~~~javascript

#include

using std::cout;

using std::endl;

template

void func(T& param) {

cout << param << endl;

}

int main() {

int num = 2019;

func(num);

return 0;

}

~~~

这样例子的编译输出都没有什么问题,但是如果我们修改成下面的调用方式呢?

~~~javascript

int main() {

func(2019);

return 0;

}

~~~

则会得到一个大大的编译错误。因为上面的模板函数只能接受左值或者左值引用(左值一般是有名字的变量,可以取到地址的),我们当然可以重载一个接受右值的模板函数,如下也可以达到效果。

~~~javascript

template

void func(T& param) {

cout << "传入的是左值" << endl;

}

template

void func(T&& param) {

cout << "传入的是右值" << endl;

}

int main() {

int num = 2019;

func(num);

func(2019);

return 0;

}

~~~

输出结果:

~~~javascript

传入的是左值

传入的是右值

~~~

第一次函数调用的是左值得版本,第二次函数调用的是右值版本。但是,有没有办法只写一个模板函数即可以接收左值又可以接收右值呢?

C++ 11中有万能引用(Universal Reference)的概念:使用`T&&`类型的形参既能绑定右值,又能绑定左值。

但是注意了:**只有发生类型推导的时候,T&&才表示万能引用**;否则,表示右值引用。

所以,上面的案例我们可以修改为:

~~~javascript

template

void func(T&& param) {

cout << param << endl;

}

int main() {

int num = 2019;

func(num);

func(2019);

return 0;

}

~~~

## 引用折叠(Universal Collapse)

万能引用说完了,接着来聊引用折叠(Univers Collapse),因为完美转发(Perfect Forwarding)的概念涉及引用折叠。一个模板函数,根据定义的形参和传入的实参的类型,我们可以有下面四中组合:

* 左值-左值 T& & # 函数定义的形参类型是左值引用,传入的实参是左值引用

* 左值-右值 T& && # 函数定义的形参类型是左值引用,传入的实参是右值引用

* 右值-左值 T&& & # 函数定义的形参类型是右值引用,传入的实参是左值引用

* 右值-右值 T&& && # 函数定义的形参类型是右值引用,传入的实参是右值引用

但是C++中不允许对引用再进行引用,对于上述情况的处理有如下的规则:

所有的折叠引用最终都代表一个引用,要么是左值引用,要么是右值引用。规则是:**如果任一引用为左值引用,则结果为左值引用。否则(即两个都是右值引用),结果为右值引用**。

即就是前面三种情况代表的都是左值引用,而第四种代表的右值引用。

## 完美转发(Perfect Forwarding)

下面接着说完美转发(Perfect Forwarding),首先,看一个例子:

~~~javascript

#include

using std::cout;

using std::endl;

template

void func(T& param) {

cout << "传入的是左值" << endl;

}

template

void func(T&& param) {

cout << "传入的是右值" << endl;

}

template

void warp(T&& param) {

func(param);

}

int main() {

int num = 2019;

warp(num);

warp(2019);

return 0;

}

~~~

猜一下,上面的输出结果是什么?

~~~javascript

传入的是左值

传入的是左值

~~~

是不是和我们预期的不一样,下面我们来分析一下原因:

`warp()`函数本身的形参是一个万能引用,即可以接受左值又可以接受右值;第一个`warp()`函数调用实参是左值,所以,`warp()`函数中调用`func()`中传入的参数也应该是左值;第二个`warp()`函数调用实参是右值,根据上面所说的引用折叠规则,warp()`函数接收的参数类型是右值引用,那么为什么却调用了调用`func()的左值版本了呢?这是因为在`warp()`函数内部,左值引用类型变为了右值,因为参数有了名称,我们也通过变量名取得变量地址。

那么问题来了,怎么保持函数调用过程中,变量类型的不变呢?这就是我们所谓的“完美转发”技术,在C++11中通过`std::forward()`函数来实现。我们修改我们的`warp()`函数如下:

~~~javascript

template

void warp(T&& param) {

func(std::forward(param));

}

~~~

则可以输出预期的结果。

linux 无线网卡驱动桥转发,引用和完美转发相关推荐

  1. C++右值引用和完美转发

    C++右值引用和完美转发 何为引用 引用必须是左值 右值引用 完美转发 move() 使用move的优点 move 左值测试 move 右值测试 注意 参考链接 看到有些同学,调用函数的时候总喜欢使用 ...

  2. C++ 万能引用 与 完美转发

    万能引用基本认识: 万能引用是一种类型,就跟int是一种类型一个道理,再次强调,万能引用是一种类型. 读者都知道,右值引用是用符号&&来表示的.右值引用主要是绑定右值的.如 int & ...

  3. 【C++11】右值引用与移动构造、万能引用与完美转发

    目录 一.右值引用 1.1 左值引用和右值引用 1.2 左值引用与右值引用比较 1.3 右值引用的使用场景和意义 二.移动构造 2.1 移动构造的实现 2.2 移动赋值 2.3 默认成员函数 2.4 ...

  4. C++11特性《 右值引用-<完美转发>、lambda表达式》

    1.右值引用 1.1移动语义 如果一个类中涉及到资源管理,用户必须显式提供拷贝构造.赋值运算符重载以及析构函数,否则编译器将 会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错,比如 ...

  5. C++左值与右值の深思——万能引用与完美转发

    目录 传统艺能

  6. C++11右值引用、完美转发foward、可变模板参数实例

    本文转载自https://blog.csdn.net/jirryzhang/article/details/82960080 #include <iostream> using names ...

  7. 【C++】右值引用、移动语义、完美转发(下篇)

    上篇中,主要讲解了右值引用和移动语义的具体定义和用法.在C++11中几乎所有的容器都实现了移动语义,以方便性能优化.本文以C++11容器中的insert方法为例,详细讲解在容器中移动语义是如何提高性能 ...

  8. [c++]-c++中的左值和右值、左值引用和右值引用、万能引用和引用折叠及完美转发

    1.左值和右值 1.1左值和右值定义 在c++中,左值是一个指向内存的东西,换句话来讲,左值有地址,保存在内存中,右值则为不指向任何地方东西,即不在内存中占有确定位置.一般来说,右值是暂时和短暂的,而 ...

  9. std:forward 完美转发

    概述:     // TEMPLATE CLASS identity template<class _Ty>     struct identity     {    // map _Ty ...

最新文章

  1. 【转】一文掌握 Linux 性能分析之网络篇(续)
  2. 深入理解文档/视图框架体系_九宫格项目开发感悟
  3. python getopts_getopts用法
  4. golang grpc demo
  5. Android下集成Paypal支付
  6. python字典替换值_python字典改变value值方法总结
  7. 苹果cms V10模板 秘趣响应式高端在线影视视频模板
  8. [转载] python迭代器
  9. End-to-End Object Detection with Transformers的部分解读
  10. android进阶指导
  11. Servlet中上传下载模板代码
  12. Visual Components 4.0仿真软件优势
  13. windows核心编程之DIPS工具学习总结
  14. 【转载】google搜索从入门到精通
  15. 计算机数值计算方法答案,数值计算方法习题解答.pdf
  16. FPGA实现数字QAM调制系统
  17. 算法之 --- 背包01版+ 挖金矿问题
  18. 《Python数据分析与挖掘实战》Chapter8中医证型关联规则挖掘笔记
  19. three.js网页demo展示
  20. matlab 实现水印添加

热门文章

  1. Java HashSet的实现原理详解
  2. GuGuFishtion(2018 Multi-University Training Contest 7)
  3. 【启智树NOIP模拟】奇偶【卢卡斯定理】【背包】【bitset】
  4. 2020 ICPC NAC
  5. D - ABC Conjecture Gym - 102798D
  6. YBTOJ:字符串题(KMP)
  7. UOJ#84-[UR #7]水题走四方【dp】
  8. P6775-[NOI2020]制作菜品【贪心,dp】
  9. P2408- 不同子串个数【SA】
  10. 【jzoj】2018.2.7NOIP普及组——某【BC】组模拟赛