右值引用应该是C++11引入的一个非常重要的技术,因为它是移动语义(Move semantics)与完美转发(Perfect forwarding)的基石:

  • 移动语义:将内存的所有权从一个对象转移到另外一个对象,高效的移动用来替换效率低下的复制,对象的移动语义需要实现移动构造函数和移动赋值运算符。
  • 完美转发:定义一个函数模板,该函数模板可以接收任意类型参数,然后将参数转发给其它目标函数,且保证目标函数接受

一、 引入的新规则

1. 规则1(引用折叠规则):如果间接的创建一个引用的引用,则这些引用就会“折叠”。在所有情况下(除了一个例外),引用折叠成一个普通的左值引用类型。一种特殊情况下,引用会折叠成右值引用,即右值引用的右值引用, T&& &&。即

  • X& &、X& &&、X&& &都折叠成X&
  • X&& &&折叠为X&&

2. 规则2(右值引用的特殊类型推断规则):当将一个左值传递给一个参数是右值引用的函数,且此右值引用指向模板类型参数(T&&)时,编译器推断模板参数类型为实参的左值引用,如

template<typename T>
void f(T&&);int main()
{int i = 42;f(i)
}

上述的模板参数类型T将推断为int&类型,而非int。

  • 若将规则1和规则2结合起来,则意味着可以传递一个左值int i给f,编译器将推断出T的类型为int&。再根据引用折叠规则 void f(int& &&)将推断为void f(int&)。
  • 从上述两个规则可以得出结论:如果一个函数形参是一个指向模板类型的右值引用,则该参数可以被绑定到一个左值上
  • 规则3:虽然不能隐式的将一个左值转换为右值引用,但是可以通过static_cast显示地将一个左值转换为一个右值。【C++11中为static_cast新增的转换功能】。

std::move()解析

class Foo {
public:std::string member;Foo(const std::string& m): member(m) {}  // Copy member.  Foo(std::string&& m): member(std::move(m)) {} // Move member.
};

上述Foo(std::string&& member)中的member是rvalue reference,但是member却是一个左值lvalue,因此在初始化列表中需要使用std::move将其转换成rvalue。

标准库中move的定义如下:

template<typename T>
typename remove_reference<T>::type&& move(T&& t)
{return static_cast<typename remove_reference<T>::type &&>(t);
}

move函数的参数T&&是一个指向模板类型参数的右值引用【规则2】,通过引用折叠,此参数可以和任何类型的实参匹配,因此move既可以传递一个左值,也可以传递一个右值;

std::move(string("hello"))调用解析:

  • 首先,根据模板推断规则,确地T的类型为string;
  • typename remove_reference<T>::type && 的结果为 string &&;
  • move函数的参数类型为string&&;
  • static_cast<string &&>(t),t已经是string&&,于是类型转换什么都不做,返回string &&;

string s1("hello"); std::move(s1); 调用解析:

  • 首先,根据模板推断规则,确定T的类型为string&;
  • typename remove_reference<T>::type && 的结果为 string&
  • move函数的参数类型为string& &&,引用折叠之后为string&;
  • static_cast<string &&>(t),t是string&,经过static_cast之后转换为string&&, 返回string &&;

从move的定义可以看出,move自身除了做一些参数的推断之外,返回右值引用本质上还是靠static_cast<T&&>完成的。

因此下面两个调用是等价的,std::move就是个语法糖。

void func(int&& a)
{cout << a << endl;
}int a = 6;
func(std::move(a));int b = 10;
func(static_cast<int&&>(b)); 

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

三、std::forward()解析

class Foo
{
public:std::string member;template<typename T>Foo(T&& member): member{std::forward<T>(member)} {}
};

传递一个lvalue或者传递一个const lvaue

  • 传递一个lvalue,模板推导之后 T = std::string&
  • 传递一个const lvaue, 模板推导之后T = const std::string&
  • T& &&将折叠为T&,即std::string& && 折叠为 std::string&
  • 最终函数为: Foo(string& member): member{std::forward<string&>(member)} {}
  • std::forward<string&>(member)将返回一个左值,最终调用拷贝构造函数

传递一个rvalue

  • 传递一个rvalue,模板推导之后 T = std::string
  • 最终函数为: Foo(string&& member): member{std::forward<string>(member)} {}
  • std::forward<string>(member) 将返回一个右值,最终调用移动构造函数;

总结:

1. std::move和std::forward本质都是转换。std::move执行到右值的无条件转换。std::forward只有在它的参数绑定到一个右值上的时候,才转换它的参数到一个右值。

2. std::move没有move任何东西,std::forward没有转发任何东西。在运行期,它们没有做任何事情。它们没有产生需要执行的代码,一byte都没有。

测试代码:

#include <iostream>
using namespace std;template<typename T>
void PrintT(T& t)
{cout << "lvaue" << endl;
}template<typename T>
void PrintT(T&& t)
{cout << "rvalue" << endl;
}template<typename T>
void TestForward(T&& v)
{PrintT(v);PrintT(std::forward<T>(v));PrintT(std::move(v));cout << "--------" << endl;
}int main()
{TestForward(1);int x = 1;TestForward(x);TestForward(std::forward<int>(x));
}

输出结果:

[ 1 ]. C++11改进我们的程序之move和完美转发
[ 2 ]. C++11 std::move和std::forward

3. C++完美转发

【C++ Primer | 16】std::move和std::forward、完美转发相关推荐

  1. 理解std::move和std::forward

    std::move c++11中提供了std::move()来将左值转换为右值引用,从而方便的使用移动语义.move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝.     ...

  2. std:forward 完美转发

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

  3. forward完美转发

    forward完美转发 std::forward是一个标准模板函数,它用于实现完美转发,即将输入的参数原封不动地传递给另一个函数,保持其左值或右值的属性. std::forward的作用是根据模板参数 ...

  4. [C/C++]关于C++11中的std::move和std::forward

    http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.html std::move是一个用于提示优化的函数,过去的c++98中,由于无法将作为右值的临时变量从 ...

  5. C++ std::move/std::forward/完美转发

    右值引用相关的几个函数:std::move, std::forward 和 成员的 emplace_back; 通过这些函数我们可以避免不必要的拷贝,提高程序性能. move 是将 对象的状态 或者 ...

  6. C++11新特性之 std::forward(完美转发)

    上篇博客对右值.右值引用都做了简要介绍. 我们也要时刻清醒,有时候右值会转为左值,左值会转为右值. (也许"转换"二字用的不是很准确) 如果我们要避免这种转换呢? 我们需要一种方法 ...

  7. 移动语义(move semantic)和完美转发(perfect forward)

    完整原文链接:https://codinfox.github.io/dev/2014/06/03/move-semantic-perfect-forward/ 移动语义(move semantic) ...

  8. [C++11]forward完美转发

    // 函数原型 template <class T> T&& forward (typename remove_reference<T>::type& ...

  9. C++ std::move()和完美转发

    std::move().std::forward<T>.模板类型推断分析 引用折叠原则和完美转发是有联系的,可以说后者是基于前者的某些特性实现的,具体来看一下. 要理解完美转发,需要了解两 ...

最新文章

  1. 用数据分析《你好,李焕英》“斐妈”爆红的真相
  2. 高等数学在计算机中的应用论文1500字,数学论文1500字-高数应用论文大一高数的应用论文字数1500左右 爱问知识人...
  3. Gym - 101334F 单调栈
  4. xaml修改后台代码的值_Django定制后台和修改模型
  5. 延迟队列Delay Queue
  6. python pipeline框架_爬虫(十六):Scrapy框架(三) Spider Middleware、Item Pipeline|python基础教程|python入门|python教程...
  7. 华为正式出售荣耀;圆通回应内鬼致 40 万条个人信息泄露;Spring Boot 2.3.6 发布|极客头条...
  8. 基于Ajax的模糊查询输入控件(补充)
  9. 最严格的身份证校验(JavaScript版)
  10. OAM 与 KubeVela 项目整体捐赠进入 CNCF,让云端应用交付更加简单
  11. 施耐德 m340 编程手册_工控安全研究系列(一)施耐德PLC 140CPU 65150
  12. poi合并单元格,没有样式
  13. 计算机音乐A谱,WPSOffice谱写音乐简谱
  14. 【编译原理】语法分析
  15. 用PS设计曲线四分环
  16. windows加载wim文件
  17. 网站开发之HTML基础知识及超链接(二)
  18. vue.config.js配置configureWebpack的optimization splitChunks页面空白
  19. SpringBoot部署
  20. Dilate Gated Convolutional Neural Network

热门文章

  1. 在数据库中outlet、code、outline为联合组件。hibarnate插入可如此插入
  2. Freemarker宏和函数的用法和区别
  3. 外中断02 - 零基础入门学习汇编语言70
  4. mysql 子字符串函数_MySQL 内置字符串函数
  5. 2012服务器系统关闭网络共享,提供网络服务的前提,Windows Server 2012如何更改高级共享设置...
  6. android fragment界面滑动切换效果,Android App中使用ViewPager+Fragment实现滑动切换效果...
  7. 个人信息管理系统代码_Thymeleaf+SpringBoot+Mybatis实现的易游网旅游信息管理系统...
  8. php for next,Nextcloud停留无限登录页面 PHP7的问题及解决方案
  9. 乒乓球比赛赛程_10月5日至10月11日中央电视台直播录播乒乓球比赛安排
  10. 异或!!不占用额外空间!!