注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

C++11及BOOST特性之二十七C++11中std::move和std::forward  

2015-10-14 14:51:04|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++11BOOST特性之二十七C++11std::movestd::forward

 

在前面好多次讲到过std::move这个函数,但是std::forward这个函数却是第一次讲,正如网上所讲,是不是后者可以替代前者呢?毕竟后者即可以使用左值又可以使用右值。估计这样想的人是不少的。

从纯技术角度来看,答案是肯定的:std::forward可以做到,std::move不是必须的。当然函数也不是必须的,我们可以写转换代码,但是我希望我们会觉得那样比较恶心。---引自“条款23:理解std::movestd::forward

至少,前者比后者要少一个模板参数不是。

 

好,为了更方便的理解这两个函数,需要做一些提前的准备:

1、引用折叠规则:

X& + & => X&

X&& + & => X&

X& + && => X&

X&& + && => X&&

2、函数模板参数推导规则(右值引用参数部分)

当函数模板的模板参数为T而函数形参为T&&(右值引用)时适用本规则。

若实参为左值 U& ,则模板参数 T 应推导为引用类型 U&

(根据引用折叠规则, U& + && => U& T&& U&,故T U&

若实参为右值 U&& ,则模板参数 T 应推导为非引用类型 U

(根据引用折叠规则, UU&& + && => U&& T&& U&&,故T UU&&,这里强制规定T U

3std::remove_referenceC++0x标准库中的元函数,其功能为去除类型中的引用

std::remove_reference<U&>::type U

std::remove_reference<U&&>::type U

std::remove_reference<U>::type U

4、以下语法形式将把表达式 t 转换为T类型的右值(准确的说是无名右值引用,是右值的一种)

static_cast<T&&>(t)

5、无名的右值引用是右值

具名的右值引用是左值。

6、注:本文中 含义为即,等价于“。

这时候就可以开始说C++中的std::forward

    // TEMPLATE CLASS identity

template<class _Ty>

    struct identity

    {    // map _Ty to type unchanged

    typedef _Ty type;

 

    const _Ty& operator()(const _Ty& _Left) const

        {    // apply identity operator to operand

        return (_Left);

        }

    };

 

    // TEMPLATE FUNCTION forward

template<class _Ty> inline

    _Ty&& forward(typename identity<_Ty>::type& _Arg)

    {    // forward _Arg, given explicitly specified type parameter

    return ((_Ty&&)_Arg);

    }

 

调用方式:      _STD forward<_Ty>(_Val));

先举一个例子:

template <typename T> void outer(T& t) {

    inner(t);

}

这里如果想调用outer这个函数,只能传入左传,直接传入右值是调用的,比如你传递的参数是一个返回对象的函数:

Temp getObject();

outer(getObject());

这样就不能使用了,而如果inner() 接收 const int& 型的参数,那 inner(5) 是可以通过编译的,但是 outer(5) 就编译不过了。因为 T 会被推导为 int int& 是不能绑定到常量 5 的。

那好再看看下面这个:

template <typename T> void outer(const T& t) {

    inner(t);

}

如果 inner()接收 int& 型参数,那就会违法 const 正确性,编译都过不了。

当然,遇到这种情况,大家都会想当然的可不可以重载啊,答案是当然可以。但是这里有一个问题,如果参数很多这事儿就不好办了,如果一两个小工程使用,还没问题,一旦上升到库,不管是小到一个公司的公用库还是大到STD的公用库,怎么办呢?它可是呈指数增长的。

你就得为每一个参数像 T1& const T1&, T2& const T2& 等这样进行重载,要重载的函数数目呈指数级增长。(VC9 SP1 tr1::bind() 就够让人感到绝望了,它为 5 个参数这么重载出了 63 个函数。如果不这么蛮干的话,没有像这里的长篇累述,我们就很难跟使用者解释为什么不能调用用 1729 这样的 ravlue 做参数的函数。为了产生出这些重载函数使用了令人作呕的预处理机制,恶心到你都不想知道它)。

C++98/03 中,转发问题是很严重的,而且本质上无解(必须求助于恶心的预处理机制,这会严重拖慢编译速度,还让代码变得难以阅读)。总算, rvalue 优雅地解决了这个问题。

总结一下来说:左值和右值最常用的是哪种情况呢:

一种是上面的返回临时对象的函数,第二种就是const &了。

 

更详细的std::move不再详述,看一下二者区别:

1std::move执行一个无条件的转化到右值。它本身并不移动任何东西;

2std::forward把其参数转换为右值,仅仅在那个参数被绑定到一个右值时;

3std::movestd::forward在运行时(runtime)都不做任何事。

 

再顺道说一下std::make_shared:

template< class T, class... Args >

shared_ptr<T> make_shared( Args... args );

它可以最多接收10个参数,将其做为构造函数的参数来创建对象指针。简单的来说,就是来匹配不同的构造函数,看下面的例子:

auto sp = std::make_shared<int>(10);

auto sp = std::make_shared<Temp>(102);

 

 shared_ptr<string> sp =  make_shared<string>("make_shared");

下面创建一个有10元素,默认值为2Vector

 shared_ptr<vector<int> > spv =  make_shared<vector<int> >(10, 2);

当然,直接传递一个对象过去同样也能达到效果,因为赋值=号是被重载了的。如下:

auto sp = std::make_shared<Temp>(std::move(ttt));

 

make_shared()能接受任意多数量的参数构造对象,一般情况下这不会成为问题。实际上,很少有如此多的参数的函数接口,即使有,那也会是一个设计的不够好的接口,应该被重构。

除了make_shared()smart_ptr库还提供一个allocate_shared(),它比make_ shared()多接受一个定制的内存分配器类型参数,其他方面都相同。

 

参考网页:

http://blog.csdn.net/tiandyoin/article/details/43604055

http://blog.csdn.net/coolmeme/article/details/44459999

http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.html

http://blog.csdn.net/zwvista/article/details/6848582

  评论这张
 
阅读(236)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017