当前位置:   article > 正文

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

std::move

 

目录

1. C++move的概念

2. C++move的特点

3. 左值、右值与左值引用、右值引用

3.1 左值和右值的概念

3.2 左值引用和右值引用

4. std::move详解

4.1 std::move简介

4.2 std::move详解

4.3 std::move实现:

4.4 std::move的优点

4.5  std::move的使用


参考:

https://blog.csdn.net/s11show_163/article/details/114296006

https://blog.csdn.net/daaikuaichuan/article/details/88371948
https://zhuanlan.zhihu.com/p/94588204
https://www.cnblogs.com/SZxiaochun/p/8017475.html
https://www.cnblogs.com/yoyo-sincerely/p/8658075.html
左值引用与右值引用,以及为什么右值引用本身是左值的问题详见:https://www.cnblogs.com/char-cheng/p/11026936.html

1. C++move的概念

一句话概括std::move ———— std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。

2. C++move的特点

  1. std::move函数可以以非常简单的方式将左值引用转换为右值引用。(左值、左值引用、右值、右值引用 参见:http://www.cnblogs.com/SZxiaochun/p/8017475.html)
  2.  通过std::move,可以避免不必要的拷贝操作。
  3. std::move是为性能而生。
  4. std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。
  5. 如string类在赋值或者拷贝构造函数中会声明char数组来存放数据,然后把原string中的 char 数组被析构函数释放,如果a是一个临时变量,则上面的拷贝,析构就是多余的,完全可以把临时变量a中的数据直接 “转移” 到新的变量下面即可。

3. 左值、右值与左值引用、右值引用

右值引用(及其支持的Move语意和完美转发)是C++0x加入的最重大语言特性之一。从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题。从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷。从库设计者的角度讲,它给库设计者又带来了一把利器。从库使用者的角度讲,不动一兵一卒便可以获得“免费的”效率提升。

3.1 左值和右值的概念

  1. 左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
  2. 右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
  3. 一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。
  4. 左值:指表达式结束后依然存在的持久对象,可以取地址,具名变量或对象 。
  5. 右值:表达式结束后就不再存在的临时对象,不可以取地址,没有名字。
  1. int a;
  2. int b;
  3. a = 3;
  4. b = 4;
  5. a = b;
  6. b = a;
  7. // 以下写法不合法。
  8. 3 = a;
  9. a + b = 4;

3.2 左值引用和右值引用

引用是C++语法做的优化,引用的本质还是靠指针来实现的。引用相当于变量的别名。引用可以改变指针的指向,还可以改变指针所指向的值。

左值引用:type &引用名 = 左值表达式;就是对左值的引用 就是给左值取别名

右值引用:type &&引用名 = 右值表达式;就是对右值的引用 就是给右值取别名

  1. int a=10; //a 是左值
  2. double b=1.3; //b 是左值
  3. //左值引用
  4. int & Ta=a; //引用左值 故 是一个左值引用
  5. double & Tb=b; //引用左值 故是一个左值引用

再比如:

  1. int a = 100;
  2. int&& b = 100;//右值引用
  3. int& c = b; //正确,b为左值
  4. int& d = 100; //错误

4. std::move详解

4.1 std::move简介

在C++11中,标准库在中提供了一个有用的函数std::move,std::move并不能移动任何东西,它唯一的功能是将一个左值引用强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

  1. #include <iostream>
  2. #include <utility>
  3. #include <vector>
  4. #include <string>
  5. int main()
  6. {
  7. std::string str = "Hello";
  8. std::vector<std::string> v;
  9. //调用常规的拷贝构造函数,新建字符数组,拷贝数据
  10. v.push_back(str);
  11. std::cout << "After copy, str is \"" << str << "\"\n"; //str输出为"Hello"
  12. //调用移动构造函数,掏空str,掏空后,最好不要使用str
  13. v.push_back(std::move(str));
  14. std::cout << "After move, str is \"" << str << "\"\n"; //str输出为空
  15. std::cout << "The contents of the vector are \"" << v[0]
  16. << "\", \"" << v[1] << "\"\n";
  17. //v[0]、v[1]都为"Hello"
  18. }

输出

  1. After copy, str is "Hello"
  2. After move, str is ""
  3. The contents of the vector are "Hello", "Hello"


4.2 std::move详解

std::move 的函数原型定义:

  1. template <typename T>
  2. typename remove_reference<T>::type&& move(T&& t)
  3. {
  4. return static_cast<typename remove_reference<T>::type &&>(t);
  5. }

首先,函数参数T&&是一个指向模板类型参数的右值引用,通过引用折叠,此参数可以与任何类型的实参匹配(可以传递左值或右值,这是std::move主要使用的两种场景)。关于引用折叠如下:

所有右值引用折叠到右值引用上仍然是一个右值引用。(A&& && 变成 A&&) 。
所有的其他引用类型之间的折叠都将变成左值引用。 (A& & 变成 A&; A& && 变成 A&; A&& & 变成 A&)。

简单来说,右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用。

  1. //原始的,最通用的版本
  2. template <typename T> struct remove_reference{
  3. typedef T type; //定义T的类型别名为type
  4. };
  5. //部分版本特例化,将用于左值引用和右值引用
  6. template <class T> struct remove_reference<T&> //左值引用
  7. { typedef T type; }
  8. template <class T> struct remove_reference<T&&> //右值引用
  9. { typedef T type; }
  10. //举例如下,下列定义的a、b、c三个变量都是int类型
  11. int i;
  12. remove_refrence<decltype(42)>::type a; //使用原版本,
  13. remove_refrence<decltype(i)>::type b; //左值引用特例版本
  14. remove_refrence<decltype(std::move(i))>::type b; //右值引用特例版本

4.3 std::move实现:

1. 首先,通过右值引用传递模板实现,利用引用折叠原理将右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。(cpr:先能够把参数类型全都接收)
2. 然后我们通过static_cast<>进行强制类型转换返回T&&右值引用,而static_cast之所以能使用类型转换,是通过remove_refrence::type模板移除T&&,T&的引用,获取具体类型T(模板偏特化)。(cpr:再把接收的参数的原引用抹除强转成右引)

4.4 std::move的优点

std::move语句可以将左值变为右值而避免拷贝构造。

std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。

4.5  std::move的使用

比如向vector中插入新元素。

  1. int a = 1;
  2. vector<int> vec;
  3. vec.push_back(move(a));

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/640039
推荐阅读
相关标签
  

闽ICP备14008679号