std::move

c++11中提供了std::move()来将左值转换为右值引用,从而方便的使用移动语义。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。 
    c++中所有容器都实现了move语义,方便我们实现性能优化。move对于拥有形如对内存、文件句柄等资源的成员的对象有效。如果是一些基本类型,比如int或char[10]数组等,如果使用move,仍然会发生拷贝(因为没有对应的移动构造函数)。

std::list<std::string> tokens;
//发生了移动构造。list的实现,将目的资源句柄赋值为源资源句柄,而将源资源句柄清空
std::list<std::string> t = std::move(tokens); 

std::forward

右值引用类型是独立于值的,一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值,并不是他原来的类型。

需要一种方法能够按照参数原来的类型转发到另一个函数,这种转发类型称为完美转发

完美转发(Perfect Forwarding),是指在函数模板中,完全依照模板的参数的类型(即保持参数的左值、右值特征),将参数传递给函数模板中调用的另外一个函数。C++11中提供了这样的一个函数std::forward,它是为转发而生的,不管参数是T&&这种未定的引用还是明确的左值引用或者右值引用,它会按照参数本来的类型转发。

#include<iostream>
using namespace std;
template<typename T>
void print(T& t)
{cout << "lvalue" << endl;
}template<typename T>
void print(T&& t)
{cout << "rvalue" << endl;
}template<typename T>
void TestForward(T && v)
{//print(v); //编译错误  不知道是哪个printprint(std::forward<T>(v));print(std::move(v));
}int main()
{TestForward(1);int x = 1;//TestForward(x); //使print(std::forward<T>(v));编译错误TestForward(std::forward<int>(x));return 0;
}
//rvalue
//rvalue
//rvalue
//rvalue

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

std::move和std::forward只不过就是执行cast的两个函数(实际上是函数模板)。std::move无条件地把它的参数转换成一个右值,而std::forward只在特定条件满足的情况下执行这个转换。

这里给出C++11中std::move实现的一个例子。它没有完全遵循标准的细节,但是很接近了。

template<typename T>   //在命名空间std中
typename remove_reference<T>::type&& move(T&& param)
{using ReturnType = typename remove_reference<T>::type&&; //别名声明return static_cast<ReturnType>(param);
}

std::move所做的所有事情就是转换它的参数为一个右值。它做的是转换,没有做move。

std::forward的情况和std::move相类似,但是std::forward是一个有条件的转换。为了理解它什么时候转换,什么时候不转换,回忆一下std::forward是怎么使用的。最常见的情况就是,一个带universal引用的参数被传给另外一个参数:

void process(const Widget& lvalArg);            // 参数为左值
void process(Widget&& rvalArg);                 // 参数为右值template<typename T>                            // 把参数传给process
void logAndProcess(T&& param)                   // 的模板
{process(std::forward<T>(param));
}

考虑一下两个logAndProcess调用,一个使用左值,另外一个使用右值:

Widget w;logAndProcess(w);               // 用左值调用
logAndProcess(std::move(w));    // 用右值调用

当我们用左值调用logAndProcess的时候,我们自然是希望这个左值作为一个左值被转发给process,然后当我们使用右值调用logAndProcess时,我们希望右值版本的process被调用。

但是param就和所有的函数参数一样,是一个左值。因此在logAndProcess内部总是调用左值版本的process。为了防止这样的事情发生,我们需要一种机制来让param在它被一个右值初始化(传给logAndProcess的参数)的时候转换成右值。这正好就是std::forward做的事情。这也就是为什么std::forward是一个条件转换:它只把用右值初始化的参数转换成右值。

我们是不是可以去掉std::move并且在所有的地方都只使用std::forward。从技术的角度来看,回答是可以:std::forward能做到所有的事情。std::move不是必须的。当然,这两个函数函数都不是“必须的”,因为我们能在使用的地方写cast,但是我希望我们能同意它们是必须的函数。

std::move的优点是方便,减少相似的错误,并且更加清晰。考虑一个类,对于这个类我们想要记录它的move构造函数被调用了多少次。

class Widget {
public:Widget(Widget&& rhs): s(std::move(rhs.s)){ ++moveCtorCalls;}
}...private:static std::size_t moveCtorCalls;std::string s;
};

为了用std::forward来实现相同的行为,代码看起来像是这样的:

class Widget {
public:Widget(Wdiget&& rhs)                    //不常见,以及不受欢迎的实现: s(std::forward<std::string>(rhs.s))//译注:为什么是std::string请看Item 1,用右值传入std::string&& str的话//推导的结果T就是std::string,用左值传入,则推导的结果T会是std::string&//然后这个T就需要拿来用作forward的模板类型参数了。//详细的解释可以参考Item28{ ++moveCtorCalls; }
};

首先注意std::move只需要一个函数参数(rhs.s),而std::forward却需要一个函数参数(rhs.s)以及一个模板类型参数(std::string)。然后注意一下我们传给std::forward的类型应该是一个非引用类型,因为我们约定好传入右值的时候要这么编码(传入一个非引用类型,看Item 28)。也就是说,这意味着std::move需要输入的东西比std::forward更少,还有,它去掉了我们传入的参数是右值时的麻烦(记住类型参数的编码)。它也消除了我们传入错误类型(比如,std::string&,这会导致数据成员用拷贝构造函数来替换move构造函数)的可能。

更加重要的是,使用std::move表示无条件转换到一个右值,然后使用std::forward表示只有引用的是右值时才转换到右值。这是两种非常不同的行为。第一个常常执行move操作,但是第二个只是传递(转发)一个对象给另外一个函数并且保留它原始的左值属性或右值属性。因为这些行为如此地不同,所以我们使用两个函数(以及函数名)来区分它们是很好的主意。

            你要记住的事

  • std::move无条件转换到右值。就其本身而言,它没有move任何东西。
  • std::forward只有在它的参数绑定到一个右值上的时候,它才转换它的参数到一个右值。
  • std::move和std::forward在运行期都没有做任何事情。

理解std::move和std::forward相关推荐

  1. 【C++ Primer | 16】std::move和std::forward、完美转发

    右值引用应该是C++11引入的一个非常重要的技术,因为它是移动语义(Move semantics)与完美转发(Perfect forwarding)的基石: 移动语义:将内存的所有权从一个对象转移到另 ...

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

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

  3. std:move基本用法和理解

    场景: C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了. ...

  4. std::move的理解

    std::move C++11开始引入了std::move,引入std::move主要是为了优化对象的生命周期,以及优化函数参数传递方式.然后又引入了一个右值得概念, 之前又有一个左值得概念.左值和右 ...

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

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

  6. C++核心准则ES.56​:只在需要将一个对象显式移动到另外的作用域时使用std::move​

    ES.56: Write std::move() only when you need to explicitly move an object to another scope ES.56:只在需要 ...

  7. C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward

    这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理 ...

  8. std:move基本用法

    std::move函数可以以非常简单的方式将左值引用转换为右值引用.(左值.左值引用.右值.右值引用 参见: 通过std::move,可以避免不必要的拷贝操作. std::move是为性能而生. st ...

  9. 详解C++移动语义std::move()

    目录 1. C++move的概念 2. C++move的特点 3. 左值.右值与左值引用.右值引用 3.1 左值和右值的概念 3.2 左值引用和右值引用 4. std::move详解 4.1 std: ...

最新文章

  1. 【SVO2.0 安装编译】Ubuntu 20.04 + Noetic
  2. 如何优化数据中心虚拟机布局
  3. GroupingComparator分组
  4. awk 分隔符_awk 中的字段、记录和变量 | Linux 中国
  5. 百度顶会论文复现(3):视频分类综述
  6. 静态链接库与动态链接库的区别
  7. oracle分布式数据库中间件,分布式数据库中间件设想
  8. 通达信副图指标公式:买卖黄金线(抄底用)
  9. 投稿期刊:机械人机交互图形图象交叉学科
  10. seay代码审计mysql插件报错_Seay源代码审计系统1.0版本发布(含下载)
  11. 练习作品9:高仿大漠工具
  12. 工资管理系统【软件综合设计报告】
  13. 亲密关系-【关键对话】-有哪些决定关系的重要议题?
  14. 伍斯特理工学院计算机研究生,伍斯特理工学院计算机工程硕士排名第52(2020年TFE Times排名)...
  15. notempty注解属于哪个依赖_@NotEmpty、@NotNull、@NotBlank注解解析
  16. 【marked is not a function】解决百度脑图“kityminder”备注报错
  17. 【搬运】1 简谱和基本知识
  18. 岛屿数量vs最大正方形
  19. 【绝对详细!不好使你顺着网线敲我!】Django3.1在Ubuntu16.04上的部署
  20. 计算机 管理 mmc 注册表,我的注册表没有MMC文件

热门文章

  1. 【转】富士通磁共振无线充电技术,几米外可充电
  2. mysql程序语句范文_MySQL基本语句
  3. 值得一用,分享4款速度快好用的手机浏览器
  4. Android 中关与类转换异常的问题。
  5. 虚拟机中输入ifconfig不显示ip地址,如何解决
  6. yolov5安装与环境配置
  7. 电大计算机毕业论文任务书范文,广播电视大学毕业设计任务书表格.doc
  8. 同步检波 matlab,实验十二 包络检波及同步检波实验
  9. 用快递单号快速查询物流退回件的单号
  10. 风华是一指流砂,苍老了一段过往年华