【移动语义和精准转发系列二】std::move和std::forward
来源:互联网 发布:iphoneairplay连接mac 编辑:程序博客网 时间:2024/05/16 09:12
在我们开始讲解std::move之前,先来介绍一个概念:引用折叠。这个概念仅用于 typedef 和 模板类型参数 中。
在模板世界中,T&& 称为Universal Reference,即通用引用。这与普通函数中形参的&&是不同的,希望不要弄混了。
对于模板函数template <typename T> void func(T&& t),对于左值实参,T&& t 推导出的T为string&;对于右值实参,T&& t 推导出的T为string。
对于左值引用X&,具有传染性,即X& &、X& &&和X&& &都折叠成类型X&;
对于右值引用X&&,只有X&& &&才折叠成X&&。
下面我们来实战一下:
template <typename T>void func(T&& val) {T t = val;t++;if(val == t) { //... }}如果传入的是一个右值42,此时T的类型为int,此时if判断永远为false;如果传入的是一个左值整型变量,则T为int&,此时,if判断永远是true。
T&&通常用于两种情况:模板转发或模板重载。对于模板重载,通常使用以下方式进行重载:
template <typename T> void func(T&&);template <typename T> void func(const T&);
好,下面进入正题。
一、先来看一下C++标准源码中是怎么实现std::move模板函数的。
//<bits/move.h> <utility>/*** @brief Convert a value to an rvalue.* @param __t A thing of arbitrary type.* @return The parameter cast to an rvalue-reference to allow moving it.*/template<typename _Tp>constexpr typename std::remove_reference<_Tp>::type&&move(_Tp&& __t) noexcept{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }其中,用到了std::remove_reference<_Tp>,这是定义在<type_traits>头文件中的。
// <type_traits> /// remove_referencetemplate<typename _Tp>struct remove_reference{ typedef _Tp type; };template<typename _Tp>struct remove_reference<_Tp&>{ typedef _Tp type; };template<typename _Tp>struct remove_reference<_Tp&&>{ typedef _Tp type; };根据模板匹配,最终reremove_reference返回的都是脱去引用后的普通类型。所以,std::remove_reference<_Tp>::type&& 一定是该类型的右值引用。虽然不能隐式将一个左值转换为右值引用,但是我们可以显式地使用static_cast将一个左值转换为右值引用。
C++标准规定,对于具名的右值引用,是一个左值;但是,对于一个无名的右值引用,是一个右值。std::move返回的正是一个无名的右值引用,所以是一个右值。这就是强制把一个左值变为右值引用的函数,当然,该函数也可以作用于右值,返回结果仍然是该右值的引用。
此时,肯定有人会说,既然这样,那为什么我要用std::move函数,我直接使用static_cast强转就OK了。是的,虽然我们可以直接使用static_cast进行右值引用强转,但是,使用标准库move函数是容易得多的方式。而且,统一使用std::move使得我们在程序中查找潜在的截断左值的代码变得很容易。
同时,move的名字冲突要比其他标准库函数的冲突频繁得多。而且,因为move执行的是非常特殊的类型操作,所以程序专门修改函数原有行为的概率非常小。对move,其实还有forward来说,冲突很多,但大多数是无意的,这一特点解释了为什么会使用带限定语的完整版本的原因。通过书写std::move而非move,我们就能明确地知道想要使用的是函数的标准库版本。
二、再来看一下C++标准源码中是怎么实现std::forward模板函数的。
//<bits/move.h> <utility> // forward (as per N3143) /** * @brief Forward an lvalue. * @return The parameter cast to the specified type. * * This function is used to implement "perfect forwarding". */ template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); } /** * @brief Forward an rvalue. * @return The parameter cast to the specified type. * * This function is used to implement "perfect forwarding". */ template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument" " substituting _Tp is an lvalue reference type"); return static_cast<_Tp&&>(__t); }
与move不同的是,forward必须通过显式模板来调用,forward返回该显式实参类型的右值引用。即:std::forward<T>的返回类型是T&&。显然,如果T是int&,则T&&仍是int&。如果T是int&&,则T&&仍是int&&。从而forward会保持实参类型的所有细节,包括const,因为对于引用来说,const都是底层的,都会予以保留。
- 【移动语义和精准转发系列二】std::move和std::forward
- C++11中移动语义(std::move)和完美转发(std::forward)
- std::move和std::forward
- std::move 和 std::forward
- C++11 std::move和std::forward
- C++11:std::move和std::forward
- 关于std::move和std::forward
- 【移动语义和精准转发系列一】移动语义
- std::move和std::forwarding完美转发。
- using声明和using指示、std::move和std::forward
- C++11尝鲜:std::move和std::forward源码分析
- C++0x,std::move和std::forward解析
- 条款23:理解std::move和std::forward
- C++11尝鲜:std::move和std::forward源码分析
- C++11:std::move和std::forward源码分析
- item 23: 理解std::move和std::forward
- C++11尝鲜:std::move和std::forward源码分析
- C++ std::move和std::forward源码分析
- 怎么做才可以把之前删除的微信聊天记录恢复
- Partition List
- poj 2763 树链剖分(单点更新,区间求值)
- jsp+js实现二级级联
- 怎样才能查看爱人的微信聊天记录QQ聊天记录
- 【移动语义和精准转发系列二】std::move和std::forward
- 如何计算IP地址的网络号和主机号?
- 怎样才能看到老公手机上已删除的QQ聊天记录
- Perl 若A则B判断的表示方法
- 3次握手4次挥手
- CIDR
- tp3.2中当点击修改时显示出默认下拉菜单中的值
- 怎样查看已删除微信聊天记录和好友聊天记录
- 自学selenium练习题第四节-页面元素基本操作