《C 0x漫谈》系列之:右值引用
2008-02-23 05:40:54来源:互联网 阅读 ()
Move语意
返回值效率问题——返回值优化((N)RVO)——mojo设施——workaround——问题定义——Move语意——语言支持
大猴子Howard Hinnant写了一篇挺棒的tutorial(a.k.a. 提案N2027),此外最初的关于rvalue-reference的若干篇提案的可读性也相当强。因此要想了解rvalue-reference的话,或去看C 标准委员会网站上的系列提案(见文章末尾的参考文献)。或阅读本文。
源起
《大史记》总看过吧?
故事,素介个样子滴…一天,小嗖风风的吹着,在一个伸手不见黑夜的五指…
我用const引用来接受参数,却把临时变量一并吞掉了。我用非const引用来接受参数,却把const左值落下了。于是乎,我就在标准的每个角落寻找解决方案,我靠!我被8.5.3打败了!…
设想这样一段代码(既然大同小异,就直接从Andrei那篇著名的文章里面拿来了):
std::vector<int> v = readFile(); |
readFile()的定义是这样的:
std::vector<int> readFile() { std::vector<int> retv; … // fill retv return retv; } |
这段代码低效的地方在于那个返回的临时对象。一整个vector得被拷贝一遍,仅仅是为了传递其中的一组int,当v被构造完毕之后,这个临时对象便烟消云散。
这完全是公然的浪费!
更糟糕的是,原则上讲,这里有两份浪费。一,retv(retv在readFile()结束之后便烟消云散)。二,返回的临时对象(返回的临时变量在v拷贝构造完毕之后也随即香消玉殒)。但是呢,对于上面的简单代码来说,大部分编译器都已能够做到优化掉这两个对象,直接把那个retv创建到接受返回值的对象,即v中去。
实际上,临时对象的效率问题一直是C 中的一个被广为诟病的问题。这个问题是如此的著名,以至于标准不惜牺牲原本简洁的拷贝语意,在标准的12.8节悍然下诏允许优化掉在函数返回过程中产生的拷贝(即便那个拷贝构造函数有副作用也在所不惜!)。这就是所谓的“Copy Elision”。
为什么(N)RVO((Named) Return Value Optimization)几乎形同虚设
还是按照Andrei的说法,只要readFile()改成这样:
… readFile() { if(/* err condition */) return std::vector<int>(); if(/* yet another err condition */) return std::vector<int>(1, 0); std::vector<int> retv; … // fill retv return retv; } |
出现这种情况,编译器一般都会乖乖放弃优化。
但对编译器来说这还不是最郁闷的一种情况,最郁闷的是:
std::vector<int> v; v = readFile(); // assignment, not copy construction |
这下由拷贝构造,变成了拷贝赋值。眼睛一眨,老母鸡变鸭。编译器只能缴械投降。因为标准只允许在拷贝构造的情况下进行(N)RVO。
为什么库方案也不是生意经
C 鬼才Andrei Alexandrescu以对C 标准的深度挖掘和利用著名,早在03年的时候(当时所谓的临时变量效率问题已在新闻组上闹了好一阵子了,相关的语言级别的解决方案也已在02年9月份粉墨登场)就在现有标准(C 98)下硬是折腾出了一个能100%解决问题的方案来。
Andrei把这个框架叫做mojo,就像一层爽身粉相同,把他往现有类上面一洒,嘿嘿…猜怎么着,不,不是“痱子去无踪”:P,是该类型的临时对象效率问题就迎刃而解了!
Mojo的唯一的问题就是使用方法过于复杂。这个复杂度,很大程度上来源于标准中的一个措辞问题(C 标准就是这样,鬼知道哪个角落的一句话能够带出一个brilliant的解决方案来,同时,鬼知道哪个角落的一句话能够抹杀一个原本简洁的解决方案)。这个问题就是我前面提到过的8.5.3问题,现在已由core language issue 391解决。
对于库方案来说,解决问题固然是首要的。但一个侵入性的,外带使用复杂性的方案必然是走不远的。因此虽然大家都不否认mojo是个天才的方案,但实际使用中难免举步维艰。这也是为什么mojo并没有被工业化的原因。
为什么改用引用传参也等于痴人说梦
void readFile(vector<int>& v){ … // fill v } |
这当然能够。
但是假如碰到操作符重载呢?
string operator (string const& s1, string const& s2); |
而且,就算是对于readFile,原先的返回vector的版本支持
BOOST_FOREACH(int i, readFile()){ … // do sth. with i } |
改成引用传参后,原本优雅的形式被破坏了,为了进行以上操作不得不引入一个新的名字,这个名字的存在只是为了应付被破坏的形式,一旦foreach操作结束他短暂的生命也随之结束:
vector<int> v; readFile(v); BOOST_FOREACH(int I, v){ } 标签: 版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 上一篇: 缓冲区溢出原理浅析连同防护 下一篇: 二级C 重点难点分析:类和对象[2]
相关文章
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设 网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源 网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它 服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护 软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷 网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash 热门词条
最新资讯
热门关注
热门标签
|