move() forward()
含义
move和forward都是C++11中引入的,它们是移动语义和完美转发实现的基石。
-
move:不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义
- 从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue)
-
forward: 不转发任何东西,也是执行左值到右值的强制类型转换,只是仅在特定条件满足时才执行该转换
- 典型使用场景:某个函数模板取用了万能引用类型为形参,随后把它传递给了另一个函数
那么问题来了,既然move不移动而只是转换类型,为什么还把它命名为move,而不是一个类似rvalue_cast之类的名字?
- 首先再重复一下,move做的是强制类型转换,把左值转换为右值,不做移动
-
而右值是可以进行移动的,所以move一个对象,就是告诉编译器该对象具备可移动的条件,这样,move表述了这样一个事实:该对象可以移动了
move很牛逼,但可能并没有想象中那么牛逼
移动语义的支持,使得我们可以以较低的成本执行以前只能复制的操作。
对于标准容器操作,如下:
std::vector<Foo> vf1;
// 放入数据
// 移动 auto vf2 = std::move(vf1); // O(1),仅是包含在vf1和vf2中的指针修改了 |
以上操作得以实现的前提是,标准容器都是将内容放在堆上的,它们内持有一个指向内存的指针。
正是由于该指针的存在,移动操作在常数时间内完成得以实现:把指针从源容器复制到目标容器,然后把源容器包含的指针置空即可。
注意,并不是所有move操作都是如此低廉的:
std::array<Foo, 10000> af1;
// 放入数据
// 移动 auto af2 = std::move(af1); // O(n),需要把af1中所有元素移动到af2 |
这是因为array对象并没有内置的指针,其内容数据是直接存储在对象内的。
所以不要总是相信下面的说法:移动容器和赋值一堆指针一样成本低廉。
总之,在下面的情况下,移动语义并不会带来什么好处:
- 没有移动操作:待移动对象没有提供移动操作,这时移动请求就变成了复制请求
- 移动不是更快:待移动对象有移动操作,但并不比复制操作更快,如采用了小型字符串优化(SSO)的小string(容器不超过15的字符串)
- 移动不可用:在移动本可以发生的语境下,要求移动操作不可抛出异常,但该操作未加上noexcept声明
- 源对象是个左值:除极少数例外,只有右值可以作为移动对象的源
与move不同,forward仅在特定条件下将实参强制转换为右值。
典型使用场景如下:
void process(const Foo& lval); void process(Foo && rval);
template<typename T> void logAndProcess(T&& param) { auto now = std::chrono::system_clock::now(); makeLogEntry("Calling 'process'", now); process(std::forward<T> param); }
|
其中:
- 形参param被传递给函数process,而process依据形参是左值还是右值进行了重载,实现不同的操作
- 我们期望,当调用logAndProcess时,传入的是左值,则以左值被process处理,右值也同样传给process处理
- forward干的就是这个事情:
Foo f;
logAndProcess(f); // 传入的是左值,process以左值来处理 logAndProcess(std::move(f)); // 传入的是右值,process以右值处理 |
也就是说,使用forward可以实现完美转发,但完美转发有时候并不完美,甚至不能转发。
下面是一些需要格外注意的情形:
-
大括号初始化物,下面两种情况会导致失败:
- 编译器无法为函数形参推导出类型
- 编译器为函数形参推导出了"错误"的类型
- 0和NULL用作空指针:它们会被推导为int,而非传递实参的指针类型,应该使用nullptr
- 仅有声明的整形static const 成员变量:能正常进行函数调用,它们没有内存,因此不能被引用,万能引用自然会失败
- 重载的函数名字和模板名字:这能通过编译,但转发函数会执行失败,因为它无法决定使用哪个重载的版本
- 位域用作函数实参:非const引用不得绑定到位域,位域是由机器字的若干任意部分组成的,无法对其直接取址。解决办法是传递位域的副本
小结
move和forward在作为c++11中引入的关键特性,使用得当可以达到很多以前很难实现的功效。
但也要注意避免使用上的陷阱,防止只是表面上使用这些特性,却没有收到任何实际的效果。
————————————————
版权声明:本文为CSDN博主「guotianqing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guotianqing/article/details/115837222