刘未鹏|C++的罗浮宫

Knowledge sharing is the best reuse

刘未鹏
刘未鹏的公告
除非特别声明,本站采用Creative Commons License许可。转载请保留作者、出处。非商业。

FeedSkyFeedBurner
或者用 鲜果 GR 抓虾 订阅。

CSDN Blog暂时不支持RSS全文输出,对此感到不便的朋友可以使用强大的greasemonkey脚本:GReader Preview Enhanced(链接),该脚本支持在GReader里面直接打开全文页面。

P.S. 我经常出没于TopLanguage讨论组

《C++的罗浮宫》5年选集(下载)

——知识分享是最大的复用


讨论问题请到TopLanguage小组

TopLanguage


gtalk/msn(邮件请发送到gmail邮箱)

pongba@gmail.com
pp_liu@msn.com

豆瓣 饭否 美味书签

搜索C++的罗浮宫上的内容(不要回车,点击Go)

twitters

books I've translated




这个Blog上都写了哪些东东

最近评论
fallening:OOps, the discussions seems much more inviting than the article.
mutemob:真是无语,什么东西非得往大了去扯,足见夸夸其谈是在校学生的通病,希望上面几位研究哲学的人,能真的老老实实做学问做下去,不要一毕业就直接去考公务员,每天想关如何运用哲学的思想如何提干往上爬,那当真就是要笑翻一片人了,像我这小程序员,学学算法,学学数学,学学思维方式,足已!
roger_77:学习了。多线程时代的共享数据读写
stonewang:很精彩,谢谢分享
rentaocc:深有感触,其实父母的赞扬对培养孩子的自信很重要
文章分类
收藏
相册
其它图片
文章中的图片
我的大头贴
C++
Andrei Alexandrescu
Andrew Lumsdaine
Bjarne Stroustrup
boost
C++ Standard Commitee
Doug Gregor
Hans J. Boehm
Jaakko Jarvi
Jeremy G. Siek
Matthew Wilson
newsgroups
boost.Developer
boost.User
comp.lang.c++.moderated
comp.std.c++
TopLanguage
Open Source
codeplex
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
fatalerror99
Glacier
realazy
SpiritEpic
TK
Yelz
YongSun
余晟|乱象&乱想
刘慈欣
吴欣安(atppp)
姬十三
张志强|阅微堂
许式伟
阮一峰
高远
鲍志云
其它
科学松鼠会
科学美国人
科幻世界
认识的朋友们
alai
chenyufei
dd
duguguiyu|Venus神庙
Googol
Joyfire
littlestone
lxwde
Matrix67
realfun
soloist
Tinyfool
windstorm
云风
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
周星星
周筠@博文视点
孟岩
张振
徐宥|4G Spaces&Web 2.3
方舟@博文视点
李笑来|Pure Pleasure
杨文博
王信文|地球没有好朋友
荣耀
莫华枫
袁泳(g9)|负暄琐话
谢东升
陈冀康@华章
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 《C++0x漫谈》系列之:右值引用(或“move语意与完美转发”)(下) 收藏

新一篇: Generic Programming - What are you, anyway? | 旧一篇: 《C++0x漫谈》系列之:右值引用(或“move语意与完美转发”)(上)

C++0x漫谈》系列之:右值引用

或“move语意与完美转发”(下)

 

By 刘未鹏(pongba)

刘言|C++的罗浮宫(http://blog.csdn.net/pongba)

 

 

C++0x漫谈》系列导言

 

这个系列其实早就想写了,断断续续关注C++0x也大约有两年余了,其间看着各个重要proposals一路review过来:rvalue-referencesconceptsmemory-modelvariadic-templatestemplate-aliasesauto/decltypeGCinitializer-lists…

 

总的来说C++09C++98相比的变化是极其重大的。这个变化体现在三个方面,一个是形式上的变化,即在编码形式层面的支持,也就是对应我们所谓的编程范式(paradigm)C++09不会引入新的编程范式,但在对泛型编程(GP)这个范式的支持上会得到质的提高:conceptsvariadic-templatesauto/decltypetemplate-aliasesinitializer-lists皆属于这类特性。另一个是内在的变化,即并非代码组织表达方面的,memory-modelGC属于这一类。最后一个是既有形式又有内在的,r-value references属于这类。

 

这个系列如果能够写下去,会陆续将C++09的新特性介绍出来。鉴于已经有许多牛人写了很多很好的tutor这里这里,还有C++标准主页上的一些introductiveproposals,如这里,此外C++社群中老当益壮的Lawrence Crowl也在google做了非常漂亮的talk)。所以我就不作重复劳动了:),我会尽量从一个宏观的层面,如特性引入的动机,特性引入过程中经历的修改,特性本身的最具代表性的使用场景,特性对编程范式的影响等方面进行介绍。至于细节,大家可以见每篇介绍末尾的延伸阅读。

 

 

右值引用导言

 

右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一,这点从该特性的提案在C++ - State of the Evolution列表上高居榜首也可以看得出来。从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题。从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷。从库设计者的角度讲,它给库设计者又带来了一把利器。从库使用者的角度讲,不动一兵一卒便可以获得“免费的”效率提升

 

 

完美转发

 

完美转发问题——不完美解决方案——模板参数推导规则——完美转发

 

动机

关于“完美转发”这个特性,其实提案N1385已经讲得非常清楚了,诸位可以直接去看N1385,如果实在还是觉得迷糊就再回来听我唠叨吧:-)

 

在泛型编码中经常出现的一个问题是(这个问题在实际中出现的场景很多,我们留到文章末尾再提,目前我们将这个特定的问题先提取孤立出来考虑):

 

如何将一组参数原封不动地转发给另一个函数

 

注意,这里所谓“原封不动”就是指,如果参数是左值,那么转发给的那个函数也要接受到一个左值,如果参数是右值,那么后者要接受到一个右值;同理,如果参数是const的,那么转发给的那个函数也要接受到一个const的值,如果是non-const的,那么后者也要接受到一个non-const的值。

 

总之一句话:

 

保持参数的左值/右值、const/non-const属性不变

 

听上去很简单吗?不妨试试看。

 

(不完美的)解决方案

假设我们要写一个泛型转发函数ff要将它的参数原封不动地转发给g(不管g的参数类型是什么):

 

template<typename T>

void f(/*what goes here?*/ t)

{

g(t);

}

 

上面的代码中,f的参数t的类型是什么?TT&const T&

 

我们一个个来分析。

 

Value

如果t的类型是T,即:

 

// take #1

template<typename T>

void f(T t)

{

g(t);

}

 

那么很显然,不能满足如下情况:

 

void g(int& i) { ++i; }

 

int myInt = 0;

 

f(myInt); // error, the value g() has incremented is a local value(a.k.a. f’s argument ‘t’)

 

即,不能将左值转发为左值。

 

Const&

如果t的类型为const T&,即:

 

// take #2

template<typename T>

void f(const T& t)

{

g(t);

}

 

则刚才的情况还是不能满足。因为g接受的参数类型为non-const引用。

 

Non-const&

那如果t的类型是T&呢?

 

// take #3

template<typename T>

void f(T& t)

{

g(t);

}

 

我们惊讶地发现,这时,如果参数是左值,那么不管是const左值还是non-const左值,f都能正确转发,因为对于const左值,T将会被推导为const UU为参数的实际类型)。并且,对于const右值,f也能正确转发(因为const引用能够绑定到右值)。只有对non-const右值不能完美转发(因为这时T&会被推导为non-const引用,而后者不能绑定到右值)。

 

即四种情况里面有三种满足了,只有以下这种情况失败:

 

void g(const int& i);

 

int source();

 

f(source()); // error

 

如果f是完美转发的话,那么f(source())应该完全等价于g(source()),后者应该通过编译,因为g是用const引用来接受参数的,后者在面对一个临时的int变量的时候应该完全能够绑定。

 

而实际上以上代码却会编译失败,因为f的参数是T&,当面对一个non-constint型右值(source()的返回值)时,会被推导为int&,而non-const引用不能绑定到右值。

 

好,现在的问题就变成,如何使得non-const右值也被正确转发,用T&f的参数类型是行不通的,唯一能够正确转发non-const右值的办法是用const T&来接受它,但前面又说过,用const T&行不通,因为const T&不能正确转发non-const左值。

 

Const& + non-const&

那两个加起来如何?

 

template<typename T>

void f(T& t)

{

g(t);

}

 

template<typename T>

void f(const T& t)

{

g(t);

}

 

一次重载。我们来分析一下。

 

对于non-const左值,重载决议会选中T&,因为绑定到non-const引用显然优于绑定到const引用(const T&)。

 

对于const左值,重载决议会选中const T&,因为显然这是个更specialized的版本。

 

对于non-const右值,T&根本就行不通,因此显然选中const T&

 

对于const右值,选中const T&,原因同第二种情况。

 

可见,这种方案完全保留了参数的左右值和const/non-const属性。

 

值得注意的是,对于右值来说,由于右值只能绑定到const引用,所以虽然const T&并非“(non-)const右值”的实际类型,但由于C++03只能用const T&来表达对右值的引用,所以这种情况仍然是完美转发。

 

组合爆炸

你可能会觉得上面的这个方案(const& + non-const&)已经是完美解决方案了。没错,对于单参的函数来说,这的确是完美方案了。

 

但是如果要转发两个或两个以上的参数呢?

 

对于每个参数,都有const T&T&这两种情况,为了能够正确转发所有组合,必须要2N次方个重载

 

比如两个参数的:

 

template<typename T1, typename T2>

void f(T1& t1, T2& t2) { g(t1, t2); }

 

template<typename T1, typename T2>

void f(const T1& t1, T2& t2) { g(t1, t2); }

 

template<typename T1, typename T2>