std::move -- 强制转化为右值
来源:互联网 发布:kingcms php 编辑:程序博客网 时间:2024/05/01 01:36
在C++11中,标准库在<utility>
中提供了一个有用的函数std::move
,这个函数的名字具有迷惑性。
实际上std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而我们可以通过右值引用使用该值,以用于移动语义。
从实现上讲,std::move基本等同于一个类型转换:
static_cast<T&&>(lvalue);
值得一提的是,被转化的左值,其生命期并没有随着左右值的转化而改变。
如果读者期望std::move转化的左值变量lvalue能立即被析构,那么肯定会失望了。
我们来看代码清单3-21所示的例子。
代码清单3-21
#include <iostream>using namespace std;class Moveable{public: int* i; Moveable() : i(new int(3)) {} ~Moveable(){ delete i; } Moveable(const Moveable& m) : i(new int(*m.i)){ } Moveable(Moveable&& m) : i(m.i){ m.i = nullptr; }};int main(){ Moveable a; Moveable c(move(a)); //会调用移动构造函数 cout << *a.i << endl;//运行时错误} // 编译选项:g++ -std=c++11 3-3-6.cpp -fno-elide-constructors
在代码清单3-21中,我们为类型Moveable定义了移动构造函数。
这个函数定义本身没有什么问题,但调用的时候,使用了Moveable c(move(a));这样的语句。这里的a本来是一个左值变量,通过std::move将其转换为右值。这样一来,a.i就被c的移动构造函数设置为指针空值。由于a的生命期实际要到main函数结束才结束,那么随后对表达式*a.i进行计算的时候,就会发生严重的运行时错误。
这是个典型误用std::move的例子。当然,标准库提供该函数的目的不是为了让程序员搬起石头砸自己的脚。事实上,要使用该函数,必须是程序员清楚需要转换的时候。比如上例中,程序员应该知道被转化为右值的a不可以再使用。
不过更多地,我们需要转换成为右值引用的还是一个确实生命期即将结束的对象。
我们来看看代码清单3-22所示的正确例子。
代码清单3-22
#include <iostream>using namespace std;class HugeMem{public: int sz; int* c; HugeMem(int size) : sz(size > 0 ? size : 1){ c = new int[sz]; } ~HugeMem(){ delete[] c; } HugeMem(HugeMem&& hm) : sz(hm.sz),c(hm.c){ hm.c = nullptr; }};class Moveable{public: int* i; HugeMem h; Moveable() :i(new int(3)), h(1024) {} ~Moveable() { delete i; } Moveable(Moveable && m) :i(m.i), h(move(m.h)) { // 强制转为右值,以调用移动构造函数 m.i = nullptr; }};Moveable GetTemp() { Moveable tmp = Moveable(); cout << hex << "Huge Mem from " << "GEtTemp" << " @" << tmp.h.c << endl; // Huge Mem from GEtTemp @01059D88 return tmp;}int main() { Moveable a(GetTemp()); cout << hex << "Huge Mem from " << "main" << " @" << a.h.c << endl; // Huge Mem from main @01059D88}// 编译选项:g++ -std=c++11 3-3-7.cpp -fno-elide-constructors
在代码清单3-22中,我们定义了两个类型:HugeMem和Moveable,其中Moveable包含了一个HugeMem的对象。
在Moveable的移动构造函数中,我们就看到了std::move函数的使用。
该函数将m.h强制转化为右值,以迫使Moveable中的h能够实现移动构造。这里可以使用std::move,是因为m.h是m的成员,既然m将在表达式结束后被析构,其成员也自然会被析构,因此不存在代码清单3-21中的生存期不对的问题。另外一个问题可能是std::move使用的必要性。这里如果不使用std::move(m.h)这样的表达式,而是直接使用m.h这个表达式将会怎样?
其实这是C++11中有趣的地方:可以接受右值的右值引用本身却是个左值。
这里的m.h引用了一个确定的对象,而且m.h也有名字,可以使用&m.h取到地址,因此是个不折不扣的左值。
不过这个左值确确实实会很快“灰飞烟灭”,因为拷贝构造函数在Moveable对象a的构造完成后也就结束了。那么这里使用std::move强制其为右值就不会有问题了。而且,如果我们不这么做,由于m.h是个左值,就会导致调用HugeMem的拷贝构造函数来构造Moveable的成员h(虽然这里没有声明,读者可以自行添加实验一下)。如果是这样,移动语义就没有能够成功地向类的成员传递。换言之,还是会由于拷贝而导致一定的性能上的损失。
事实上,为了保证移动语义的传递,程序员在编写移动构造函数的时候,应该总是记得使用std::move转换拥有形如堆内存、文件句柄等资源的成员为右值,这样一来,如果成员支持移动构造的话,就可以实现其移动语义。而即使成员没有移动构造函数,那么接受常量左值的构造函数版本也会轻松地实现拷贝构造,因此也不会引起大的问题。
《深入理解C++11》
- std::move -- 强制转化为右值
- std::move:强制转化为右值
- std::move:强制转化为右值
- C++ 学习(右值引用, std::move)
- std::move和右值引用
- 关于std::move与右值引用
- C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward
- 【原】C++ 11右值引用 std::move的作用
- C++ 11 右值引用以及std::move
- C++ std::move原理&右值引用存在的必要性
- 条款25:对右值引用使用std::move,对统一引用使用std::forward
- Item 25: 对右值引用使用std::move,对universal引用则使用std::forward
- c++ 11 移动语义、std::move 左值、右值、将亡值、纯右值、右值引用
- 右值引用 MOVE
- std::move C++11 标准新特性: 右值引用与转移语义
- std::move C++11 标准新特性: 右值引用与转移语义
- C++11之右值引用、完美转换(Perfect Forwarding)、std::move
- Effective Modern C++ 条款25 对右值引用使用std::move,对通用引用使用std::forward
- ReactJs入门教程
- Problem-I
- JavaScript概述
- 专题四 第八道题
- ThreadLocal学习笔记
- std::move -- 强制转化为右值
- springmvc数据绑定
- 使用WPF创建SignalR服务端
- hibernate映射属性
- ASCII码表完整版
- linux c++消息队列(ftok,msgget,msgsnd,msgrcv,msgctl)
- 浏览器兼容性问题大汇总
- Mybatis 传入多个参数的方法
- JavaScript -- switch,case,break,default