C++0x,std::move和std::forward解析
1.std::move
1.1std::move是如何定义的
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
1.2 std::move是如何工作的
1.2.1传入一个右值
a.如果传入是一个右值string,比如“hello”,推断出_Tp类型为string
b.std::remove_reference<_Tp>::type的类型依旧为string
c.move函数的返回类型为string&&
d.move函数的参数类型为string&&
e.static_cast显式转换类型为string&&
1.2.2传入一个左值
a.推断出_Tp的类型为string&
b.std::remove_reference<_Tp>::type的类型为string
c.move函数的返回类型为string&&
d.move函数的参数类型为string& &&,会折叠为string&
e.static_cast显式转换类型为string&&
1.3引用折叠
a.X& &,X& &&和X&& &都折叠为X&
b.X&& && 折叠为X&&
2.std::forward
2.1std::forward是如何定义的
/**
* @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);
}
2.2std::forward是如何工作的
2.2.1_Tp类型是左值引用
a.如果中转函数的实参是左值string,_Tp的类型为string&,std::remove_reference<_Tp>::type为string
b.forward函数参数__t的类型折叠后为string&
c.string& && (_Tp&&)折叠后依旧为左值引用string&
d.forword函数的返回为string&
2.2.2_Tp类型是右值
a.如果中转实参是右值sting,_Tp的类型为sting,std::remove_reference<_Tp>::type为string
b.forward函数参数__t的类型为string&&
c.forword函数的返回为string&&
2.3 模版重载
此处存在错误!因为存在模版重载机制,所以左值使用第一个版本,而右值选择第二个版本。
更正:完美转发时 ,只有左值,因为右值引用一旦有名字,就是左值!!!必定选择第一个版本,在非完美转发场景下存在如下规则
A a;
std::forward<A&>(std::move(a)); //版本 2 error
A&& a = std::forward<A&&>(A()); //版本 2 ok
A&& b = std::forward<A>(A()); // 版本 2 ok
上述代码抛出异常。
3.STL转发的例子
// shared_ptr.h
// This constructor is non-standard, it is used by allocate_shared.
template<typename _Alloc, typename... _Args>
shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: __shared_ptr<_Tp>(__tag, __a, std::forward<_Args>(__args)...)
{ } template<typename _Tp, typename _Alloc, typename... _Args>
inline shared_ptr<_Tp>
allocate_shared(const _Alloc& __a, _Args&&... __args)
{
return shared_ptr<_Tp>(_Sp_make_shared_tag(), __a,
std::forward<_Args>(__args)...);
} template<typename _Tp, typename... _Args>
inline shared_ptr<_Tp>
make_shared(_Args&&... __args)
{
typedef typename std::remove_const<_Tp>::type _Tp_nc;
return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
std::forward<_Args>(__args)...);
}
//shared_ptr_base.h
#ifdef __GXX_RTTI
protected:
// This constructor is non-standard, it is used by allocate_shared.
template<typename _Alloc, typename... _Args>
__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: _M_ptr(), _M_refcount(__tag, (_Tp*), __a,
std::forward<_Args>(__args)...)
{
// _M_ptr needs to point to the newly constructed object.
// This relies on _Sp_counted_ptr_inplace::_M_get_deleter.
void* __p = _M_refcount._M_get_deleter(typeid(__tag));
_M_ptr = static_cast<_Tp*>(__p);
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
}
#else
template<typename _Alloc>
struct _Deleter
{
void operator()(_Tp* __ptr)
{
typedef allocator_traits<_Alloc> _Alloc_traits;
_Alloc_traits::destroy(_M_alloc, __ptr);
_Alloc_traits::deallocate(_M_alloc, __ptr, );
}
_Alloc _M_alloc;
}; template<typename _Alloc, typename... _Args>
__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: _M_ptr(), _M_refcount()
{
typedef typename _Alloc::template rebind<_Tp>::other _Alloc2;
_Deleter<_Alloc2> __del = { _Alloc2(__a) };
typedef allocator_traits<_Alloc2> __traits;
_M_ptr = __traits::allocate(__del._M_alloc, );
__try
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2070. allocate_shared should use allocator_traits<A>::construct
__traits::construct(__del._M_alloc, _M_ptr,
std::forward<_Args>(__args)...);
}
__catch(...)
{
__traits::deallocate(__del._M_alloc, _M_ptr, );
__throw_exception_again;
}
__shared_count<_Lp> __count(_M_ptr, __del, __del._M_alloc);
_M_refcount._M_swap(__count);
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
}
#endif
//new_allocator.h
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
上面STL例子,还应用了可变参数模版和转发参数包语法,需要进一步了解。本人水平有限,如果错误请指正谢谢。
C++0x,std::move和std::forward解析的更多相关文章
- C++11 std::move和std::forward
下文先从C++11引入的几个规则,如引用折叠.右值引用的特殊类型推断规则.static_cast的扩展功能说起,然后通过例子解析std::move和std::forward的推导解析过程,说明std: ...
- item 23: 理解std::move和std::forward
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 根据std::move和std::forward不 ...
- C++11中std::move、std::forward、左右值引用、移动构造函数的测试
关于C++11新特性之std::move.std::forward.左右值引用网上资料已经很多了,我主要针对测试性能做一个测试,梳理一下这些逻辑,首先,左值比较熟悉,右值就是临时变量,意味着使用一次就 ...
- std::move()和std::forward()
std::move(t)负责将t的类型转换为右值引用,这种功能很有用,可以用在swap中,也可以用来解决完美转发. std::move()的源码如下 template<class _Ty> ...
- 关于C++11中的std::move和std::forward
std::move是一个用于提示优化的函数,过去的c++98中,由于无法将作为右值的临时变量从左值当中区别出来,所以程序运行时有大量临时变量白白的创建后又立刻销毁,其中又尤其是返回字符串std::st ...
- [C/C++]关于C++11中的std::move和std::forward
http://www.cnblogs.com/cbscan/archive/2012/01/10/2318482.html http://blog.csdn.net/fcryuuhou/article ...
- 透彻理解C++11新特性:右值引用、std::move、std::forward
目录 浅拷贝.深拷贝 左值.右值 右值引用类型 强转右值 std::move 重新审视右值引用 右值引用类型和右值的关系 函数参数传递 函数返还值传递 万能引用 引用折叠 完美转发 std::forw ...
- Item 25: 对右值引用使用std::move,对universal引用则使用std::forward
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 右值引用只能绑定那些有资格被move的对象上去.如 ...
- 一文带你详细介绍c++中的std::move函数
前言 在探讨c++11中的Move函数前,先介绍两个概念(左值和右值) 左值和右值 首先区分左值和右值 左值是表达式结束后依然存在的持久对象(代表一个在内存中占有确定位置的对象) 右值是表达式结束时不 ...
随机推荐
- StringJoiner
示例一 public class StringJoinerTest1 { public static void main(String[] args) { StringJoiner joiner = ...
- building for production...Killed
npm run build报错 building for production...Killed 原理 按照他人的说法是,服务器内存不够用了,这样就给他配置一个单独的内存出来就解决了 解决方法 sud ...
- OC和C语言比较
说明:比较记忆相对来说更容易熟练记得牢固,理解了C语言相对来说OC也不太难,OC是C语言的扩展,向下兼容C语言. 源文件后缀名比较 1.C语言源文件 .h:头文件 .c:源文件 .o:目标文件 .ou ...
- Swift_枚举
Swift_枚举 点击查看源码 空枚举 //空枚举 enum SomeEnumeration { // enumeration definition goes here } 枚举基本类型 //枚举基本 ...
- MySQL数据库 : 基本语句
mysql -uroot -p 登陆数据库 select now(); 显示当前时间 \q 退出 show databases; 显示数据库 create database 数据库名 charset= ...
- 判断ARP欺骗
转自http://bbs.51cto.com/thread-904594-1.html 网关是服务器或者单独主机设备的话 如果网关是服务器或者单独主机设备的话查询网关MAC地址要简单一些,我们只需要在 ...
- React Native开发之expo中camera的基本使用
之前做RN项目没调用过本地摄像头,今天下班早,做了一个简单的小demo:主要实现的功能:点击拍照按钮进入拍照界面,点击flip进行前后摄像头转换,点击开始拍照实现拍照功能(没写保存到本地的功能,大家可 ...
- 【Spark】源码分析之RDD的生成及stage的切分
一.概述 Spark源码整体的逻辑(spark1.3.1): 从saveAsTextFile()方法入手 -->saveAsTextFile() --> saveAsHadoopFile ...
- spark----词频统计(一)
利用Linux系统中安装的spark来统计: 1.选择目录,并创建一个存放文本的目录,将要处理的文本保存在该目录下以供查找操作: ① cd /usr/local ②mkdir mycode ③ cd ...
- 使用C语言协助办公_02批量修改学生信息
最新录制了一个使用C语言批量修改学生信息的视频,主要是讲了如何处理文件路径以及批量修改的思路.灵感来源于需要将整个17级社保照片按规格改名字.具体见:https://chuanke.baidu.com ...