用户操作
[留言]  [发消息]  [加为好友] 
订阅我的博客
XML聚合    FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
pongba的公告
除非特别声明,本站采用<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/cn/" rel="license"><img width="16" height="16" border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_o_cclogo.gif" alt="Creative Commons License"/></a>许可。转载请保留作者、出处。非商业。 <br/> <h3>重要公告</h3> <div style="background:#000000;color:#ffffff;">本博客已经迁移至 http://mindhacks.cn ,此处保留作为镜像,但不保证一定同步更新所有内容。原订阅 http://blog.csdn.net/pongba/rss.aspx (原始 Feed) 的朋友请转为订阅永久 feed : http://mindhacks.cn/feed/</div> <h3>关于</h3> 我经常在 @<a href="https://groups.google.com/group/pongba" target="_blank">TopLanguage</a> | @<a href="https://twitter.com/pongba" target="_blank">Twitter</a> | @<a href="http://www.douban.com/people/pongba/" target="_blank">Douban</a> <br/><br/> <style type="text/css"> #accordionmenu ul{ list-style-type: none; } #accordionmenu li{ list-style-type: none; padding: 0px; } #accordionmenu ul, #accordionmenu ul ul { list-style: none; margin: 0; padding: 0; } #accordionmenu ul li { display: inline; } #accordionmenu ul a { display: block; text-decoration: none; } #accordionmenu ul li ul li{ vertical-align: bottom; } #accordionmenu ul li a { background: #333; color: #fff; padding: 0.5em; } #accordionmenu ul li a:hover { background: #000; } #accordionmenu ul li a, #accordionmenu ul li a:hover { border-bottom: 1px dotted #C0C0C0; } #accordionmenu ul li ul li a { background: #ccc; color: #000; padding-left: 20px; } #accordionmenu ul li ul li a:hover { background: #aaa; border-left: 5px #000 solid; padding-left: 15px; } #accordionmenu h3 { font-weight: bold; text-align: center; font-size: 14pt; border-bottom: none; } #accordionmenu h1 { font-size: 14px; font-weight: bold; text-align: center; } #accordionmenu p { text-align: right; } .sharing { border:1px dotted #AAAAAA; padding:10px; } </style> <div id="accordionmenu"> <h3>《C++的罗浮宫》5年选集</h3> <p>——知识分享是最大的复用</p> 下载地址:<a href="http://download.csdn.net/source/630320" target="_blank">csdn资源频道</a>|<a href="http://www.mediafire.com/?r9y9aynxy84" target="_blank">mediafire</a> <ul> <li> <a target="_blank" href="http://del.icio.us/pongbablog/machine-learning">机器学习与人工智能</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/21/2958094.aspx">数学之美番外篇:平凡而又神奇的贝叶斯方法</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/11/2915005.aspx">机器学习与人工智能学习资源导引</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/essay">学习与思考</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/12/18/3549560.aspx">如何清晰地思考:近一年来业余阅读的关于思维方面的知识结构整理(附大幅思维导图)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/14/3776586.aspx">什么是你的不可替代性和核心竞争力</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/16/3796771.aspx">锤子和钉子</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2009/01/18/3829054.aspx">逃出你的肖申克(一):一定要亲身经历了之后才能明白?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/08/2625115.aspx">一直以来伴随我的一些学习习惯(part1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/20/2681668.aspx">一直以来伴随我的一些学习习惯(part2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/09/17/2942482.aspx">一直以来伴随我的一些学习习惯(part3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/12/05/3456240.aspx">一直以来伴随我的一些学习习惯(part4)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/10/29/3176250.aspx">方法论、方法论——程序员的阿喀琉斯之踵</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/07/07/2622713.aspx">知其所以然地学习(以算法学习为例)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/08/2260812.aspx">阅读与思考</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/03/03/2143245.aspx">Failing To See the Big Picture - Mistakes we make when learning programming</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/02/25/2118907.aspx">你的技术之路</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/01/04/2025830.aspx">鱼是最后一个看到水的</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/10/1736333.aspx">玩的岁月</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/06/24/1664597.aspx">我不想与我不能</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/05/24/1624382.aspx">学习密度与专注力</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/problem-solving">解题与思维</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/18/2302905.aspx">跟波利亚学解题(rev#3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/05/2513263.aspx">学习与记忆</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/05/07/2412144.aspx">解题思维杂感三则</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/05/2513247.aspx">动态规划与排列组合</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/04/09/2270171.aspx">今天我们思考</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/math">数学与计算理论</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2006/10/15/1336028.aspx">康托尔、哥德尔、图灵——永恒的金色对角线(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/02/1912466.aspx">数学之美番外篇:进化论中的概率论</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/13/2544933.aspx">数学之美番外篇:快排为什么那样快</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/cplusplus">C++与技术</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/11/1780545.aspx">为什么C++</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/04/1916385.aspx">为什么C++(C++之父的观点)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/12/11/1930150.aspx">学习C++:实践者的方法</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/05/16/1611593.aspx">你应当如何学习C++(以及编程)(rev#1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/01/10/2034207.aspx">C++之父元旦专访(8+13个问题,关于C++的学习使用和未来)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/08/1732055.aspx">争论C++前你应当知道什么(rev#1)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/10/08/1815742.aspx">错误处理:为何、何时、如何(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2006/01/26/588638.aspx">锁无关的数据结构</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2006/01/29/589864.aspx">锁无关的数据结构与Hazard指针</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/06/13/2544894.aspx">泛型编程:源起、实现与意义</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/29/1715263.aspx">Generic Programming - What are you, anyway?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2004/09/01/90642.aspx">深度探索元函数</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2003/10/24/19130.aspx">为什么C++编译器不能支持对模板的分离式编译</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/articles/1561194.aspx">《C++0x漫谈》系列</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/08/04/1726031.aspx">《C++0x漫谈》之:Concept! Concept!</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/10/1778748.aspx">《C++0x漫谈》之:瘦身前后——兼谈语言进化</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/09/07/1776255.aspx">《C++0x漫谈》之:Auto的故事</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/10/1684519.aspx">《C++0x漫谈》之:Move语意与完美转发(上)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/07/18/1697636.aspx">《C++0x漫谈》之:Move语意与完美转发(下)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/06/20/1659952.aspx">《C++0x漫谈》之:多线程内存模型</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/articles/1561110.aspx">《Boost源码剖析》系列</a> <ul> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1561006.aspx">《Boost源码剖析》之:多重回调机制signal(上)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1561083.aspx">《Boost源码剖析》之:多重回调机制signal(下)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560773.aspx">《Boost源码剖析》之:泛型回调function(rev#3)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560754.aspx">《Boost源码剖析》之:Tuple Types(rev#2)</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/11/1560738.aspx">《Boost源码剖析》之:泛型多维数组multi_array</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2004/08/24/82811.aspx">《Boost源码剖析》之:泛型指针any(rev#2)</a></li> </ul> </li> <li> <a target="_blank" href="http://del.icio.us/pongbablog/essay">其它</a> <ul> <li><a href="http://del.icio.us/pongbablog/%E5%8F%A4%E9%BE%99">读古龙的岁月</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/10/07/1813553.aspx">我们为什么这么容易受骗?</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2007/04/26/1586144.aspx">Blog外挂:妙用del.icio.us实现“站内相关文章”</a></li> <li><a href="http://blog.csdn.net/pongba/archive/2008/03/12/2172677.aspx">找啊找啊找朋友</a></li> </ul> </li> <li> <a href="http://blog.csdn.net/pongba/MyArticles.aspx">所有文章</a> </li> </ul> </div> <br/> <style type="text/css"> .delicious-posts ul {list-style-type:none; margin: 0 0 1em 0; padding: 0 } .delicious-extended{margin:0;padding:0 0 0.25em 0} .module-list-item .delicious-posts ul{margin:0;padding:0} .module-list-item .delicious-posts h2, .module-list-item .delicious-posts li:first-child{margin-top:0} </style> <h3>讨论问题请到<a href="http://groups.google.com/group/pongba">TopLanguage</a>综合技术讨论组</h3> <center><a href="http://groups.google.com/group/pongba"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_01-sm-c.gif" alt="TopLanguage"></img></a></center> <br/> <h3>精彩言论@TopLanguage</h3> <div id="toplang_comments" class="sharing"> </div> <br/> <h3>pongba的共享阅读<a href="http://delicious.com/pongba" target="_blank">@Delicious</a></h3> <div id="delicious_shared_reading" class="sharing"> </div> <br/> <h3>pongba<a href="http://twitter.com/pongba" target="_blank">@Twitter</a></h3> <div id = "twitter_update_list" class="sharing"> </div> <script type="text/javascript"> function twitterCallback2(C){var A=[];for(var D=0;D<C.length;D++){var E=C[D].user.screen_name;var B=C[D].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g,function(F){return'<a href="'+F+'">'+F+"</a>"}).replace(/\B@([_a-z0-9]+)/ig,function(F){return F.charAt(0)+'<a href="http://www.twitter.com/'+F.substring(1)+'">'+F.substring(1)+"</a>"});A.push("<li><span>"+B+'</span> <a style="font-size:85%" href="http://twitter.com/'+E+"/statuses/"+C[D].id+'">'+relative_time(C[D].created_at)+"</a></li>")}document.getElementById("twitter_update_list").innerHTML=A.join("")}function relative_time(C){var B=C.split(" ");C=B[1]+" "+B[2]+", "+B[5]+" "+B[3];var A=Date.parse(C);var D=(arguments.length>1)?arguments[1]:new Date();var E=parseInt((D.getTime()-A)/1000);E=E+(D.getTimezoneOffset()*60);if(E<60){return"less than a minute ago"}else{if(E<120){return"about a minute ago"}else{if(E<(60*60)){return(parseInt(E/60)).toString()+" minutes ago"}else{if(E<(120*60)){return"about an hour ago"}else{if(E<(24*60*60)){return"about "+(parseInt(E/3600)).toString()+" hours ago"}else{if(E<(48*60*60)){return"1 day ago"}else{return(parseInt(E/86400)).toString()+" days ago"}}}}}}}; </script> <br/> <h3>pongba在读<a href="http://www.douban.com/people/pongba/" target="_blank">@豆瓣</a></h3> <script type="text/javascript" src="http://www.douban.com/service/badge/pongba/?show=dolist&amp;select=favorite&amp;n=9&amp;columns=3&amp;cat=book" ></script> <br/> <h3>gtalk/msn(邮件请发送到gmail邮箱)</h3> <a href="mailto:pongba@gmail.com"> <img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_gmail.gif" alt="pongba@gmail.com"></img> </a> <br/> <a href = "mailto:pp_liu@msn.com"> <img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_msn.gif" alt="pp_liu@msn.com"></img> </a> <br/> <h3>搜索(不要回车,点击Go)</h3> <script language="javascript"> function search () { var wd=document.getElementsByName("q")[0].value; var link="http://www.baidu.com/s?ie=utf-8&tn=baidulocal&cl=3&ct=2097152&si=blog.csdn.net%2Fpongba" + "&wd=" + wd ; window.open(link); } </script> <input type="text" onkeydown="javascript:var kCode = event.keyCode ? event.keyCode : event.which ? event.which : event.charCode; if (kCode == 13){search();}" name="q"/> <input type="button" onclick="javascript:search()" value="Go"/> <br/> <h3>pongba翻译的</h3> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2007/12/03/1914425.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_bc_small.JPG"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2007/03/09/1525361.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_wewlc_small.jpg"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2005/11/22/534336.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_ecs_small.JPG"></img></a> </center> <br/> <center> <a href="http://blog.csdn.net/pongba/archive/2005/08/15/455350.aspx" target="_blank"><img border="0" src="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/186037/o_ic_small.JPG"></img></a> </center> <script type="text/javascript"> (function(){ var toplang = document.createElement('div'); toplang.setAttribute('id', 'toplang'); toplang.setAttribute('style', 'float:right; margin:0pt 1em; position:absolute; right:2%; top:15px;') var a1 = document.createElement('a'); a1.setAttribute('href', 'http://groups.google.com/group/pongba'); a1.setAttribute('target', '_blank'); a1.setAttribute('title', 'TopLanguage'); a1.setAttribute('style', 'text-decoration: underline;'); a1.appendChild(document.createTextNode('TopLanguage讨论组')); toplang.appendChild(a1); toplang.appendChild(document.createTextNode(' | ')); a1 = document.createElement('a'); a1.setAttribute('href', 'http://groups.google.com/group/pongba/web/toplanguage-subscription-howto'); a1.setAttribute('target', '_blank'); a1.setAttribute('title', '加入TopLanguage'); a1.setAttribute('style', 'text-decoration: underline;'); a1.appendChild(document.createTextNode('加入')); toplang.appendChild(a1); var header = document.getElementById('csdnblog_header'); if(header) { header.appendChild(toplang); } })(); </script> <script type="text/javascript"> /* test if _item_ is in _set_ */ function IsIn(item, set) { for(var i = 0; i < set.length; ++i){ if(set[i]&&(item == set[i]))return true; } return false; } /* get the link to the current page, strip out the # part if the page is opened through a comment link */ function GetThisURL() { var thisURL = document.URL; var end = thisURL.indexOf('#'); if(end != -1){ thisURL = thisURL.slice(0, end); } return thisURL; } /* select from _set_ all the elements that don't exist in _filter_ */ function Unique(set, filter) { var newSet = new Array(); for(var i = 0; i < set.length; i++){ if(!IsIn(set[i], filter)) newSet.push(set[i]); } return newSet; } /* randomly select _num_ items from _set_ */ function RandomSelection(set, num) { var selected = new Array(); var positiveSelecting = (num <= set.length/2); var ajustedNum = (positiveSelecting ? num : (set.length - num)); for(var n = 0; n < ajustedNum;){ var randIdx = Math.floor(Math.random()*(set.length)); if(!IsIn(set[randIdx], selected)){ selected.push(set[randIdx]); n++; } } if(!positiveSelecting){ selected = Unique(set, selected); } return selected; } function getElements(classname, tagname, root) { // If no root was specified, use the entire document // If a string was specified, look it up if (!root) root = document; else if (typeof root == "string") root = document.getElementById(root); // if no tagname was specified, use all tags if (!tagname) tagname = "15-"; // Find all descendants of the specified root with the specified tagname var all = root.getElementsByTagName(tagname); // If no classname was specified, we return all tags if (!classname) return all; // Otherwise, we filter the element by classname var elements = []; // Start with an empty array for(var i = 0; i < all.length; i++) { var element = all[i]; if (isMember(element, classname)) // isMember() is defined below elements.push(element); // Add class members to our array } // Note that we always return an array, even if it is empty return elements; // Determine whether the specified element is a member of the specified // class. This function is optimized for the common case in which the // className property contains only a single classname. But it also // handles the case in which it is a list of whitespace-separated classes. function isMember(element, classname) { var classes = element.className; // Get the list of classes if (!classes) return false; // No classes defined if (classes == classname) return true; // Exact match // We didn't match exactly, so if there is no whitespace, then // this element is not a member of the class var whitespace = /\s+/; if (!whitespace.test(classes)) return false; // If we get here, the element is a member of more than one class and // we've got to check them individually. var c = classes.split(whitespace); // Split with whitespace delimiter for(var i = 0; i < c.length; i++) { // Loop through classes if (c[i] == classname) return true; // and check for matches } return false; // None of the classes matched } } var HTTP = {}; HTTP._factories = [ function() { return new XMLHttpRequest(); }, function() { return new ActiveXObject("Msxml2.XMLHTTP"); }, function() { return new ActiveXObject("Microsoft.XMLHTTP"); } ]; HTTP._factory = null; HTTP.newRequest = function() { if (HTTP._factory != null) return HTTP._factory(); for(var i = 0; i < HTTP._factories.length; i++) { try { var factory = HTTP._factories[i]; var request = factory(); if (request != null) { HTTP._factory = factory; return request; } } catch(e) { continue; } } HTTP._factory = function() { throw new Error("XMLHttpRequest not supported"); } HTTP._factory(); } </script> <script type="text/javascript"> function showTopPosts(availElem, suffix) { if(!availElem)return; availElem.parentNode.insertBefore(document.createElement('br'), availElem); var topPostsDiv = document.createElement('div'); topPostsDiv.setAttribute('id', 'topPosts'+suffix); topPostsDiv.innerHTML = '<div style="margin: 0px 3% 0px 3%;background:#F9F9F9 none repeat scroll 0% 0%; border:1px solid #CCCCCC; color:#5B5B5B; font-size:10pt; font-style:normal; line-height:150%; padding:0px 10px;">' + '<table width="100%"><tbody><tr valign="middle"><td width="12" style="font-weight: bold;">置<br/>顶<br/>文<br/>章</td>' + '<td><ul id="' + 'recomm1' + suffix + '"></ul></td><td><ul id="' + 'recomm2' + suffix + '"></ul></td>' + '</tr></tbody></table></div>'; availElem.parentNode.insertBefore(topPostsDiv, availElem); var numTopPosts = 0; var i = 0; for(i = 0 ; i < Delicious.posts.length; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ numTopPosts++; } } var cond1 = document.getElementById('recomm1'+suffix); cond1.innerHTML = ''; var count = 0; for(i = 0; count < numTopPosts/2; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ count++; cond1.innerHTML += '<li><a href = "' + Delicious.posts[i].u + '">' + Delicious.posts[i].d + '</a></li>'; } } var cond2 = document.getElementById('recomm2'+suffix); cond2.innerHTML = ''; for(; i < Delicious.posts.length; ++i){ if(IsIn('topPost', Delicious.posts[i].t)){ cond2.innerHTML += '<li><a href = "' + Delicious.posts[i].u + '">' + Delicious.posts[i].d + '</a></li>'; } } availElem.parentNode.insertBefore(document.createElement('br'), availElem); } function doShowTopPosts() { showTopPosts(document.getElementById('Post.ascx_ViewPost_PreviousAndNextEntriesUp'), 'Up'); showTopPosts(document.getElementById('Post.ascx_ViewPost_PreviousAndNextEntriesDown'), 'Down'); } </script> <script type="text/javascript"> function urlToId(url) { var a1 = url.split('/'); var a2 = a1[a1.length - 1]; var a3 = a2.split('.'); return a3[0]; } var getFeedbackCountReq; var myBlogPosts = {}; var hotPostsContainer; function showHotPosts(ct) { hotPostsContainer = ct; for(i = 0; i < Delicious.posts.length; ++i){ var postID = urlToId(Delicious.posts[i].u); myBlogPosts[postID] = {url:Delicious.posts[i].u, title:Delicious.posts[i].d, feedbackCount:0}; } var idList = ''; for(var id in myBlogPosts){ idList += id + ','; } getFeedbackCountReq = HTTP.newRequest(); getFeedbackCountReq.onreadystatechange = handleGetFeedbackCountReq; getFeedbackCountReq.open('GET', '/NewCount.aspx?FeedbackCountStack=' + idList); getFeedbackCountReq.send(null); } function handleGetFeedbackCountReq() { if(getFeedbackCountReq.readyState==4){ if(getFeedbackCountReq.status==200){ handleGetFeedbackCountReq2(getFeedbackCountReq.responseText); } } } function handleGetFeedbackCountReq2(feedbackStr) { var feedbackList = feedbackStr.split(','); var i; for(i = 0; i < feedbackList.length; ++i){ var IDFeedbackCountPair = feedbackList[i].split('='); myBlogPosts[IDFeedbackCountPair[0]].feedbackCount = IDFeedbackCountPair[1]; } var myBlogPostsVector = []; for(var id in myBlogPosts){ myBlogPostsVector.push({"id":id, "url":myBlogPosts[id].url, "title":myBlogPosts[id].title, "feedbackCount":myBlogPosts[id].feedbackCount}); } myBlogPostsVector.sort(compareByFeedbackCount); var title = document.createElement('h3'); title.appendChild(document.createTextNode('本blog评论最多文章')); hotPostsContainer.appendChild(title); var NGList = ['http://blog.csdn.net/pongba/archive/2004/08/26/84978.aspx', 'http://blog.csdn.net/pongba/archive/2004/11/26/195052.aspx', 'http://blog.csdn.net/pongba/archive/2004/11/26/195075.aspx']; var maxNumHotPosts = 12 + NGList.length; if(myBlogPosts.length < maxNumHotPosts) maxNumHotPosts = myBlogPosts.length; var ul = document.createElement('ul'); for(i = 0; i < maxNumHotPosts; ++i){ if(IsIn(myBlogPostsVector[i].url, NGList))continue; var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href', myBlogPostsVector[i].url); a.appendChild(document.createTextNode(myBlogPostsVector[i].title)); li.appendChild(a); var sup = document.createElement('sup'); sup.setAttribute('style', 'color:red;'); sup.appendChild(document.createTextNode('(' + myBlogPostsVector[i].feedbackCount + ')')); li.appendChild(sup); ul.appendChild(li); } hotPostsContainer.appendChild(ul); } function compareByFeedbackCount(post1, post2) { return post2.feedbackCount - post1.feedbackCount; } function compareById(post1, post2){ return post2.id - post1.id; } </script> <script type="text/javascript"> function GetPositionalDiv() { var elems = getElements('articalinfo', 'p'); if(elems.length==0||elems.length>1)return null; return elems[0]; } /* get tags of this post */ function GetTags() { var thisURL = GetThisURL(); var tags; for(var i = 0; i < Delicious.posts.length ; i++){ var post = Delicious.posts[i]; if(post.u == thisURL){ tags = []; for(var j = 0; j < post.t.length; ++j){ if(post.t[j] == 'topPost')continue; tags.push(post.t[j]); } break; } } return tags; } /* tags are ordered by their importance, we assume that the more specific a tag is, the more important it will be, and the smaller a tag is, the more specific it will be */ function TagOrderPred(tag1, tag2) { return Delicious.tags[tag1] - Delicious.tags[tag2]; } /* find articles related to a particular _tag_ */ function BuildRelatedArticleList(tag) { var list = new Array(); for(var i = 0, post; post = Delicious.posts[i]; i++){ if(IsIn(tag, post.t) && post.u!=GetThisURL()){ list.push(post); } } return list; } function ShowRelatedArticles(ct, tags) { if(!tags)return null; var maxSize = 12; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_related_posts'); var selectedPosts = new Array(); var currentSize = 0; tags.sort(TagOrderPred); // rating tags for(var k = 0; k < tags.length; k++){ var tag = tags[k]; var relatedPosts = BuildRelatedArticleList(tag); // remove those already selected ones relatedPosts = Unique(relatedPosts, selectedPosts); // if there's no related posts to this particular tag, keep going on if(relatedPosts.length == 0) continue; // make sure the total size doesn't exeed the maxSize if(currentSize + relatedPosts.length > maxSize){ var roomLeft = maxSize - currentSize; relatedPosts = RandomSelection(relatedPosts, roomLeft); } // add'em to the collection of selected posts selectedPosts = selectedPosts.concat(relatedPosts); var promptTxt = document.createTextNode('from tag '); var promptTxtDecorated = document.createElement('font'); promptTxtDecorated.setAttribute('size', '+1'); promptTxtDecorated.appendChild(promptTxt); ul.appendChild(promptTxtDecorated); var taglnk = document.createElement('a'); taglnk.setAttribute('href', 'http://del.icio.us/pongbablog/'+tag); taglnk.appendChild(document.createTextNode(tag)); var taglnkDecorated = document.createElement('font'); taglnkDecorated.setAttribute('size', '+2'); taglnkDecorated.appendChild(taglnk); ul.appendChild(taglnkDecorated); // create list element for every related posts for(var i = 0; i < relatedPosts.length; i++){ var li = document.createElement('li'); var a = document.createElement('a'); a.setAttribute('href', relatedPosts[i].u); a.appendChild(document.createTextNode(relatedPosts[i].d)); li.appendChild(a); ul.appendChild(li); } var br = document.createElement('br'); ul.appendChild(br); currentSize += relatedPosts.length; if(currentSize >= maxSize)break; } var title = document.createElement('h3'); title.appendChild(document.createTextNode("本blog相关文章")); ct.appendChild(title); ct.appendChild(ul); return selectedPosts; } function ShowRandomArticles(ct, filter) { if(!filter) filter = []; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_random_posts'); var numArticles = Delicious.posts.length; var indices = new Array(); var maxNumShown = 12; if(numArticles < maxNumShown){ maxNumShown = numArticles; } for(var i = 0; i < maxNumShown;){ var randIndex = Math.floor(Math.random()*numArticles); if((Delicious.posts[randIndex].u!=GetThisURL())&& (!IsIn(randIndex, indices))&& (!IsIn(Delicious.posts[randIndex], filter))){ indices.push(randIndex); var a = document.createElement('a'); a.setAttribute('href', Delicious.posts[randIndex].u); a.appendChild(document.createTextNode(Delicious.posts[randIndex].d)); var li = document.createElement('li'); li.appendChild(a); ul.appendChild(li); i++; } } var title = document.createElement('h3'); title.appendChild(document.createTextNode("本blog随机文章")); ct.appendChild(title); ct.appendChild(ul); } function ShowTags(ct, tags) { if(!tags)return; var ul = document.createElement('ul'); ul.setAttribute('id', 'my_related_tags'); for(var i = 0; i < tags.length; i++){ var a = document.createElement('a'); a.setAttribute('href', 'http://del.icio.us/pongbablog/'+tags[i]); a.appendChild(document.createTextNode(tags[i] + '(' + Delicious.tags[tags[i]] + ')')); var aDecorated = document.createElement('font'); aDecorated.setAttribute('size', '+1'); aDecorated.appendChild(a); var li = document.createElement('li'); li.appendChild(aDecorated); li.appendChild(document.createTextNode(' ')); ul.appendChild(li); } var title = document.createElement('h3'); title.appendChild(document.createTextNode('本文相关tags')); ct.appendChild(title); ct.appendChild(ul); } function createDivWithId(id) { var element = document.createElement('div'); element.setAttribute('id', id); return element; } function Prepare() { var ct = GetPositionalDiv(); if(!ct)return false; ct.parentNode.insertBefore(createDivWithId('blog_related_posts'), ct); ct.parentNode.insertBefore(createDivWithId('blog_hot_posts'), ct); ct.parentNode.insertBefore(createDivWithId('blog_related_tags'), ct); ct.parentNode.insertBefore(createDivWithId('blog_random_posts'), ct); ct.parentNode.insertBefore(document.createElement('br'), ct); return true; } function ShowRelatedAll() { if(!Prepare())return; var tags = GetTags(); var alreadySelected = ShowRelatedArticles(document.getElementById('blog_related_posts'), tags); showHotPosts(document.getElementById('blog_hot_posts')); ShowTags(document.getElementById('blog_related_tags'), tags); ShowRandomArticles(document.getElementById('blog_random_posts'), alreadySelected); } </script> <h3>这个Blog上都写了哪些东东</h3> <div id="tags_cloud_delicious"></div> <script type="text/javascript"> function ShowTagCloud() { var ts = Delicious.tags; if(!ts)return; var html = ''; var ta = 0,tz=36; function s(a, b, i, x) { if(a>b){ var m=(a-b)/Math.log(x), v=a-Math.floor(Math.log(i)*m); }else{ var m=(b-a)/Math.log(x), v=Math.floor(Math.log(i)*m+a); } return v; } html += ('<style type="text/css">.delicious-tags{font-family:arial,sans-serif}.delicious-tags a{text-decoration:none}.delicious-tags a:hover{text-decoration:underline}.delicious-tags ul{list-style-type:none;margin:0;padding:0; text-align:justify}.delicious-cloud li{display:inline;text-align:justify;background-image:none !important;padding:0;margin:0}.delicious-cloud .delicious-tag-count{padding-left:0.2em;font-size:12px}.delicious-cloud li:before{content:"" !important}</style>'); var ca=[135,206,235], cz=[0,0,255], c=[]; html += ('<div class="delicious-tags" id="delicious-tags-pongbablog"><ul class="delicious-cloud">'); for(var t in ts){ if(t == 'topPost')continue; for(var i=0; i<3; i++) c[i]=s(ca[i], cz[i], ts[t]-ta, tz); var fs = s(12,35,ts[t]-ta,tz); html += ('<li style="font-size:'+fs+'px;line-height:1; display:inline;"><a style="color:rgb('+c[0]+','+c[1]+','+c[2]+')" href="http://del.icio.us/pongbablog/'+encodeURIComponent(t).replace('%2F','/')+'">'+t+'</a><span class="delicious-tag-count" style="font-size:12px;line-height:'+fs+'px;">('+ts[t]+')</span> </li>'); } html += ('</ul><br/>'); html += ('</div>'); document.getElementById('tags_cloud_delicious').innerHTML = html; } </script> <script type="text/javascript"> function doAllWorks(tags) { if(!tags){return;} if(typeof(Delicious) == 'undefined'||typeof(Delicious.posts) == 'undefined') { return; } Delicious.tags = tags; doShowTopPosts(); ShowRelatedAll(); ShowTagCloud(); } </script> <script type="text/javascript"> function runOnLoad(f) { if (runOnLoad.loaded) f( ); // If already loaded, just invoke f( ) now. else runOnLoad.funcs.push(f); // Otherwise, store it for later } runOnLoad.funcs = []; // The array of functions to call when the document loads runOnLoad.loaded = false; // The functions have not been run yet. // Run all registered functions in the order in which they were registered. // It is safe to call runOnLoad.run( ) more than once: invocations after the // first do nothing. It is safe for an initialization function to call // runOnLoad( ) to register another function. runOnLoad.run = function( ) { if (runOnLoad.loaded) return; // If we've already run, do nothing for(var i = 0; i < runOnLoad.funcs.length; i++) { try { runOnLoad.funcs[i]( ); } catch(e) { /* An exception in one function shouldn't stop the rest */ } } runOnLoad.loaded = true; // Remember that we've already run once. delete runOnLoad.funcs; // But don't remember the functions themselves. delete runOnLoad.run; // And forget about this function too! }; // Register runOnLoad.run( ) as the onload event handler for the window var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = runOnLoad.run; } else { window.onload = function() { runOnLoad.run(); oldonload(); }; } </script> <script type="text/javascript"> if (typeof window.Delicious == 'undefined') window.Delicious = {}; if(typeof(Delicious) == 'undefined') Delicious = {}; Delicious.Linkrolls = function() { return { render: function(o, posts) { var out = []; w = function (s) { out.push(s); }; posts.sort(function(b,a) { return ( (a.dt > b.dt) ? 1 : (a.dt < b.dt) ? -1 : 0 ); }); w('<div class="delicious-posts">'); w('<ul>'); for(var i=0,p;( i<o.count ) && ( p=posts[i] );i++){ w('<li class="delicious-post delicious-'+(i%2?'even':'odd')+'">'); w('<a class="delicious-link"' + ' href="' + p.u + '">'+this.htmlEscape(p.d)+'</a> '); if(p.n) w('<p class="delicious-extended">'+this.htmlEscape(p.n)+'</p>'); w('</li>'); } w('</ul>'); w('</div>'); return out.join(''); }, htmlEscape: function(s) { return (''+s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }, EOF: null }; }(); Delicious.Linkrolls_CB_51902 = function(posts) { var myhtml = Delicious.Linkrolls.render({"user":"pongba","usertags":"shared-reading","version":"2","count":"15","sort":"date","extended":true,"icon":"s","name":true,"showadd":true,"BASE_URL":"http:\/\/delicious.com","STATIC_URL":"http:\/\/l.yimg.com\/hr\/10374"}, posts); document.getElementById('delicious_shared_reading').innerHTML = myhtml; }; Delicious.Linkrolls_TL_SHARE = function(posts) { var myhtml = Delicious.Linkrolls.render({"user":"pongba","usertags":"toplanguage","version":"2","count":"15","sort":"date","extended":true,"icon":"s","name":true,"showadd":true,"BASE_URL":"http:\/\/delicious.com","STATIC_URL":"http:\/\/l.yimg.com\/hr\/10374"}, posts); document.getElementById('toplang_comments').innerHTML = myhtml; }; function DeliciousPrepare1(posts) { Delicious.posts = posts; } </script> <script type="text/javascript"> if (typeof window.Delicious == "undefined") window.Delicious = {}; Delicious.BLOGBADGE_DEFAULT_CLASS = 'delicious-blogbadge-line'; if (typeof(Delicious)=='undefined') Delicious = {}; if (typeof(Delicious.BlogBadge)=='undefined') { if (!Object.prototype.hasOwnProperty) { Object.prototype.hasOwnProperty = function(p) { return (typeof this[p] != 'undefined') && (this.constructor.prototype[p] !== this[p]); } } Delicious.BlogBadge = function() { return { DEBUG: false, url_root: Delicious.URL_ROOT || 'http://delicious.com', post_url_root: Delicious.POST_URL_ROOT || 'http://delicious.com/save', json_url_root: Delicious.JSON_URL_ROOT || 'http://feeds.delicious.com/v2/json/urlinfo', static_url_root: Delicious.STATIC_URL_ROOT || 'http://l.yimg.com/hr', default_class: "delicious-blogbadge-tall", posts_count: 5, did_init: false, badges: {}, log: function(msg) { if (this.DEBUG) if (window.console && console.log) console.log(msg); }, init: function() { if (arguments.callee.done) return; arguments.callee.done = true; if (Delicious.BLOGBADGE_DEFAULT_CLASS) { Delicious.BlogBadge.default_class = Delicious.BLOGBADGE_DEFAULT_CLASS; } this.badges = {}; // HACK: Packed version of md5.js is at the bottom of this file. // this.insertExtJSLibs(); this.insertBadgeCSS(); }, onload: function() { if (arguments.callee.done) return; arguments.callee.done = true; if(this.noAvailDiv)return; this.fetchBadgeJSON(); }, insertExtJSLibs: function() { this.forEach([ this.static_url_root+'/js/md5.js' ], function(u) { var scr = document.createElement("script"); scr.setAttribute("type", "text/javascript"); scr.setAttribute("src", u); document.getElementsByTagName("head").item(0).appendChild(scr); }); }, insertBadgeCSS: function() { var bcss = document.getElementById('delicious-blogbadge-css'); if (!bcss) { bcss = this.el('link', { 'id': 'delicious-blogbadge-css', 'type': 'text/css', 'rel': 'stylesheet', 'href': this.static_url_root+'/css/blogbadge.css' }, []); document.getElementsByTagName('head')[0].appendChild(bcss); } bcss = null; }, register: function(divid, url, title, opts) { var hash = hex_md5(url); var badge = { 'divid': divid, 'url': url, 'hash': hash, 'title':title, 'opts': opts||{} }; this.badges[hash] = badge; this.replaceChildNodes( document.getElementById(badge.divid), this.renderPostButton(badge) ); }, writeBadge: function(divid, url, title, opts, classname) { if (!classname) classname = this.default_class; var deliciousDivWrapper = document.createElement('div'); deliciousDivWrapper.innerHTML = '<div class="'+classname+'" id="'+divid+'"></div>'; var availElem = GetPositionalDiv(); if(!availElem) { this.noAvailDiv = true; return; } availElem.parentNode.insertBefore(document.createElement('br'), availElem); availElem.parentNode.insertBefore(deliciousDivWrapper, availElem); availElem.parentNode.insertBefore(document.createElement('br'), availElem); this.register(divid, url, title, opts); }, fetchBadgeJSON: function() { var json_url = this.json_url_root + "?callback=Delicious.BlogBadge.handleBadgeJSON" + "&noCacheIE="+(new Date()).getTime(); var showposts = false; for (hash in this.badges) { if (!this.badges.hasOwnProperty(hash)) continue; var badge = this.badges[hash]; json_url += "&hash="+encodeURIComponent(hash); if (!showposts && badge.opts.showposts) { showposts = true; json_url += "&showposts=yes&count="+this.posts_count; } } // TODO: Make work in XHTML pages with namespaces. var scr = document.createElement("script"); scr.setAttribute("type", "text/javascript"); scr.setAttribute("src", json_url); scr.setAttribute("id", 'delicious-blogbadge-json'); this.script_ele = scr; document.getElementsByTagName("head").item(0).appendChild(this.script_ele); }, handleBadgeJSON: function(data) { this.log(data); this.log(this.badges); for (var i=0,rec; rec=data[i]; i++) { var badge = this.badges[rec.hash]; if (badge && !badge.loaded) { this.buildBadge(badge, rec); badge.loaded = true; } } for (hash in this.badges) { if (!this.badges.hasOwnProperty(hash)) continue; var badge = this.badges[hash]; if (badge.loaded) continue; this.buildBadge(badge, { url: badge.url, total_posts: 0, top_tags: [] }); } document.getElementsByTagName("head").item(0).removeChild(this.script_ele); this.script_ele = null; }, buildBadge: function(badge, data) { badge.data = data; this.replaceChildNodes( document.getElementById(badge.divid), this.renderBadge(badge) ); }, buildPostURL: function(badge) { return this.post_url_root + '?url='+encodeURIComponent(badge.url)+ '&title='+encodeURIComponent(badge.title || '')+ '&jump=yes&partner=delbg'; }, renderPostButton: function(badge) { var el = this.bind(this.el); var post_url = this.buildPostURL(badge); return [ el('a', {'class':'save-to-link', 'href':post_url, 'title':'Bookmark '+badge.title+' on Delicious'}, [ el('span', {'class':'save-to-link-label'}, ['bookmark this on Delicious']) ]) ]; }, renderBadge: function(badge) { var el = this.bind(this.el); var map = this.bind(this.map); var data = badge.data; var total_posts = data.total_posts || 0; var top_tags = []; for (t in data.top_tags) { if (!data.top_tags.hasOwnProperty(t)) continue; top_tags.push(t); } top_tags.sort(function(a,b) { return ( data.top_tags[b] - data.top_tags[a] ); }); var posts = []; if (badge.opts.showposts) { for (var i=0, post; (i<this.posts_count) && (post=badge.data.posts[i]); i++) { posts.push(post); } } var post_bookmark_url = this.buildPostURL(badge); return [ this.renderPostButton(badge), (total_posts > 0) ? el('a', {'class':'url-link', 'href':this.url_root+'/url/'+data.hash}, [ el('span', {'class':'post-count-label-before'}, ['saved by ']), el('span', {'class':'post-count'}, [ total_posts ]), el('span', {'class':'post-count-label-after'}, (total_posts>1) ? [' other people'] : [' other person'] ), ]) : [ el('a', {'class':'empty-save-to-link', 'href':post_bookmark_url}, [ el('span', {'class':'empty-save-to-link-label'}, ['save this']), ]), el('span', {'class':'empty-message'}, [ 'be the first to bookmark this page!' ]) ], (! (total_posts > 0 && top_tags.length > 0) ) ? '' : [ el('div', {'class':'top-tags-container'}, [ el('span', {'class':'top-tags-title'}, ['tags: ']), el('ul', {'class':'top-tags'}, map(function(t) { return [ el('li', {}, [ el('a', {'href':this.url_root+'/tag/'+t, 'title':t}, [t]) ]), ' ' ]; }, top_tags) ) ]) ], (! (badge.opts.showposts && posts.length) ) ? '' : [ el('span', {'class':'latest-posts'}, [ el('span', {'class':'latest-posts-label'}, ['recent bookmarks: ']), el('ul', {}, map( function(post) { return el('li', {'class':'xfolkentry'}, [ el('a', {'class':'taggedlink', 'href':post.u}, [post.n]), (!post.d) ? '' : el('blockquote', {'class':'description'}, [post.d]), el('div', {'class':'meta'}, [ el('span', {'class':'meta-label-by'}, [' by ']), el('a', {'class':'author', 'href':this.url_root+'/'+post.un}, [post.un]), (!post.t.length) ? '' : [ el('span', {'class':'meta-label-to'}, [' to ']), el('ul', {'class':'tags'}, map(function(t) { return [ el('li', {'class':'tag'}, [ el('a', {'rel':'tag', 'href':this.url_root+'/'+post.un+'/tag/'+t}, [t]) ]), ' ']; }, post.t)) ], el('span', {'class':'meta-label-at'}, [' at ']), el('abbr', {'class':'created','title':post.dt+'Z'}, [post.dt+'Z']), ]) ]); }, posts)) ]), ], ' ', el('br',{},[]) ]; }, bind: function(func) { var obj = this; return function() { return func.apply(obj, arguments) }; }, forEach: function(list, fn) { fn = this.bind(fn); for (var i=0; i<list.length; i++) fn(list[i]); }, filter: function(fn, list) { var rv = []; fn = this.bind(fn); for (var i=0; i<list.length; i++) if (fn(list[i])) rv[rv.length] = list[i]; return rv; }, map: function(fn, list) { var rv = []; fn = this.bind(fn); for (var i=0; i<list.length; i++) rv[rv.length] = fn(list[i]); return rv; }, replaceChildNodes: function(parent, nodes) { while (parent.firstChild) parent.removeChild(parent.firstChild); return this.appendChildNodes(parent, nodes); }, appendChildNodes: function(parent, nodes) { if (!nodes || !nodes.length) return; for (var i=0; i<nodes.length; i++) { var node = nodes[i]; if (!node) continue; if (node.nodeType) parent.appendChild(node); else if ( (typeof(node) == 'object') && node.length) this.appendChildNodes(parent, node); else parent.appendChild(document.createTextNode(''+node)); } }, el: function(name, attrs, nodes) { var elem = document.createElement(name); if (attrs) for (k in attrs) { if (!attrs.hasOwnProperty(k)) continue; var v = attrs[k]; if (k.substring(0, 2) == "on") { if (typeof(v) == "string") { v = new Function(v); } elem[k] = v; } else { elem.setAttribute(k, v); } switch(k) { // MSIE seems to want this. case 'class': elem.className = v; break; } } if (nodes) this.appendChildNodes(elem, nodes); return elem; }, EOF:null }; }(); } // HACK: Packed version of http://pajhome.org.uk/crypt/md6/md5.js eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('e 1i=0;e 1g="";e p=8;f 1f(s){g K(A(D(s),s.o*p))}f 1w(s){g S(A(D(s),s.o*p))}f 1N(s){g L(A(D(s),s.o*p))}f 2b(w,v){g K(I(w,v))}f 2a(w,v){g S(I(w,v))}f 2c(w,v){g L(I(w,v))}f 2i(){g 1f("1R")=="1O"}f A(x,G){x[G>>5]|=1U<<((G)%E);x[(((G+1V)>>>9)<<4)+14]=G;e a=24;e b=-1Y;e c=-1X;e d=2h;z(e i=0;i<x.o;i+=16){e Y=a;e W=b;e X=c;e 1b=d;a=l(a,b,c,d,x[i+0],7,-2d);d=l(d,a,b,c,x[i+1],12,-28);c=l(c,d,a,b,x[i+2],17,29);b=l(b,c,d,a,x[i+3],22,-1T);a=l(a,b,c,d,x[i+4],7,-1Z);d=l(d,a,b,c,x[i+5],12,2j);c=l(c,d,a,b,x[i+6],17,-1P);b=l(b,c,d,a,x[i+7],22,-1Q);a=l(a,b,c,d,x[i+8],7,1S);d=l(d,a,b,c,x[i+9],12,-25);c=l(c,d,a,b,x[i+10],17,-26);b=l(b,c,d,a,x[i+11],22,-2f);a=l(a,b,c,d,x[i+12],7,2e);d=l(d,a,b,c,x[i+13],12,-2g);c=l(c,d,a,b,x[i+14],17,-27);b=l(b,c,d,a,x[i+15],22,1M);a=h(a,b,c,d,x[i+1],5,-1t);d=h(d,a,b,c,x[i+6],9,-1s);c=h(c,d,a,b,x[i+11],14,1u);b=h(b,c,d,a,x[i+0],20,-1v);a=h(a,b,c,d,x[i+5],5,-1r);d=h(d,a,b,c,x[i+10],9,1q);c=h(c,d,a,b,x[i+15],14,-1l);b=h(b,c,d,a,x[i+4],20,-1k);a=h(a,b,c,d,x[i+9],5,1m);d=h(d,a,b,c,x[i+14],9,-1n);c=h(c,d,a,b,x[i+3],14,-1p);b=h(b,c,d,a,x[i+8],20,1o);a=h(a,b,c,d,x[i+13],5,-1x);d=h(d,a,b,c,x[i+2],9,-1y);c=h(c,d,a,b,x[i+7],14,1I);b=h(b,c,d,a,x[i+12],20,-1H);a=k(a,b,c,d,x[i+5],4,-1J);d=k(d,a,b,c,x[i+8],11,-1K);c=k(c,d,a,b,x[i+11],16,1L);b=k(b,c,d,a,x[i+14],23,-1G);a=k(a,b,c,d,x[i+1],4,-1F);d=k(d,a,b,c,x[i+4],11,1A);c=k(c,d,a,b,x[i+7],16,-1z);b=k(b,c,d,a,x[i+10],23,-1B);a=k(a,b,c,d,x[i+13],4,1C);d=k(d,a,b,c,x[i+0],11,-1E);c=k(c,d,a,b,x[i+3],16,-1D);b=k(b,c,d,a,x[i+6],23,1W);a=k(a,b,c,d,x[i+9],4,-2z);d=k(d,a,b,c,x[i+12],11,-2F);c=k(c,d,a,b,x[i+15],16,2G);b=k(b,c,d,a,x[i+2],23,-2D);a=m(a,b,c,d,x[i+0],6,-2B);d=m(d,a,b,c,x[i+7],10,2I);c=m(c,d,a,b,x[i+14],15,-2O);b=m(b,c,d,a,x[i+5],21,-2M);a=m(a,b,c,d,x[i+12],6,2J);d=m(d,a,b,c,x[i+3],10,-2H);c=m(c,d,a,b,x[i+10],15,-2A);b=m(b,c,d,a,x[i+1],21,-2p);a=m(a,b,c,d,x[i+8],6,2q);d=m(d,a,b,c,x[i+15],10,-2o);c=m(c,d,a,b,x[i+6],15,-2n);b=m(b,c,d,a,x[i+13],21,2m);a=m(a,b,c,d,x[i+4],6,-2r);d=m(d,a,b,c,x[i+11],10,-2k);c=m(c,d,a,b,x[i+2],15,2y);b=m(b,c,d,a,x[i+9],21,-2t);a=u(a,Y);b=u(b,W);c=u(c,X);d=u(d,1b)}g H(a,b,c,d)}f F(q,a,b,x,s,t){g u(Z(u(u(a,q),u(x,t)),s),b)}f l(a,b,c,d,x,s,t){g F((b&c)|((~b)&d),a,b,x,s,t)}f h(a,b,c,d,x,s,t){g F((b&d)|(c&(~d)),a,b,x,s,t)}f k(a,b,c,d,x,s,t){g F(b^c^d,a,b,x,s,t)}f m(a,b,c,d,x,s,t){g F(c^(b|(~d)),a,b,x,s,t)}f I(w,v){e C=D(w);1d(C.o>16)C=A(C,w.o*p);e P=H(16),V=H(16);z(e i=0;i<16;i++){P[i]=C[i]^2L;V[i]=C[i]^2N}e 1c=A(P.18(D(v)),19+v.o*p);g A(V.18(1c),19+2C)}f u(x,y){e O=(x&N)+(y&N);e 1a=(x>>16)+(y>>16)+(O>>16);g(1a<<16)|(O&N)}f Z(T,M){g(T<<M)|(T>>>(E-M))}f D(n){e B=H();e J=(1<<p)-1;z(e i=0;i<n.o*p;i+=p)B[i>>5]|=(n.2l(i/p)&J)<<(i%E);g B}f L(B){e n="";e J=(1<<p)-1;z(e i=0;i<B.o*E;i+=p)n+=2s.2x((B[i>>5]>>>(i%E))&J);g n}f K(r){e U=1i?"2w":"2v";e n="";z(e i=0;i<r.o*4;i++){n+=U.R((r[i>>2]>>((i%4)*8+4))&1e)+U.R((r[i>>2]>>((i%4)*8))&1e)}g n}f S(r){e 1h="2u+/";e n="";z(e i=0;i<r.o*4;i+=3){e 1j=(((r[i>>2]>>8*(i%4))&Q)<<16)|(((r[i+1>>2]>>8*((i+1)%4))&Q)<<8)|((r[i+2>>2]>>8*((i+2)%4))&Q);z(e j=0;j<4;j++){1d(i*8+j*6>r.o*E)n+=1g;2K n+=1h.R((1j>>6*(3-j))&2E)}}g n}',62,175,'||||||||||||||var|function|return|md5_gg|||md5_hh|md5_ff|md5_ii|str|length|chrsz||binarray|||safe_add|data|key|||for|core_md5|bin|bkey|str2binl|32|md5_cmn|len|Array|core_hmac_md5|mask|binl2hex|binl2str|cnt|0xFFFF|lsw|ipad|0xFF|charAt|binl2b64|num|hex_tab|opad|oldb|oldc|olda|bit_rol|||||||||concat|512|msw|oldd|hash|if|0xF|hex_md5|b64pad|tab|hexcase|triplet|405537848|660478335|568446438|1019803690|1163531501|187363961|38016083|701558691|1069501632|165796510|643717713|373897302|b64_md5|1444681467|51403784|155497632|1272893353|1094730640|681279174|722521979|358537222|1530992060|35309556|1926607734|1735328473|378558|2022574463|1839030562|1236535329|str_md5|900150983cd24fb0d6963f7d28e17f72|1473231341|45705983|abc|1770035416|1044525330|0x80|64|76029189|1732584194|271733879|176418897|||||1732584193|1958414417|42063|1502002290|389564586|606105819|b64_hmac_md5|hex_hmac_md5|str_hmac_md5|680876936|1804603682|1990404162|40341101|271733878|md5_vm_test|1200080426|1120210379|charCodeAt|1309151649|1560198380|30611744|2054922799|1873313359|145523070|String|343485551|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789|0123456789abcdef|0123456789ABCDEF|fromCharCode|718787259|640364487|1051523|198630844|128|995338651|0x3F|421815835|530742520|1894986606|1126891415|1700485571|else|0x36363636|57434055|0x5C5C5C5C|1416354905'.split('|'),0,{})) </script> <script type="text/javascript"> runOnLoad(function() { var note1 = document.createElement('div'); note1.innerHTML = '<div style="background:#000000;color:#ffffff;text-align:center;padding:10px 0px;">本博客已经迁移至 http://mindhacks.cn ,此处保留作为镜像,但不保证一定同步更新所有内容。原订阅 http://blog.csdn.net/pongba/rss.aspx (原始 Feed) 的朋友请转为订阅永久 feed : http://mindhacks.cn/feed/</div>'; var ct = GetPositionalDiv(); if(ct){ ct.parentNode.insertBefore(document.createElement('br'), ct); ct.parentNode.insertBefore(note1, ct); ct.parentNode.insertBefore(document.createElement('br'), ct); } }); </script> <script type="text/javascript"> Delicious.BlogBadge.init(); runOnLoad(function() { Delicious.BlogBadge.writeBadge("delicious-blogbadge-1", location.href, document.title, {}); Delicious.BlogBadge.onload(); }); </script> <script type="text/javascript"> runOnLoad(function( ){ function newScript( src ) { var script = document.createElement("script"); script.setAttribute("type", "text/javascript"); script.setAttribute("src", src); return script; } document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongbablog?count=100&callback=DeliciousPrepare1")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/tags/pongbablog?count=100&callback=doAllWorks")); document.body.appendChild(newScript("http://twitter.com/statuses/user_timeline/pongba.json?callback=twitterCallback2&count=8")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongba/shared-reading?count=15&callback=Delicious.Linkrolls_CB_51902")); document.body.appendChild(newScript("http://feeds.delicious.com/v2/json/pongba/toplanguage?count=15&callback=Delicious.Linkrolls_TL_SHARE")); }); </script>
文章分类
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
Ant
codeplex
Danga
Google AJAX Search API
Google Code Prettify
Google Web Toolkit
Hadoop
MS shared source initiative
notepad++
STLSoft
不认识的朋友们
Delphij
fatalerror99
flow with the life
Glacier
jimaxsoft
lifesinger@淘宝UED
Mr. 6
realazy
Robbin
SpiritEpic
TK
wuyizi
Yelz
丁丁虫
付翀
冰云
刘慈欣
卢昌海
吴欣安(atppp)
周爱民
和菜头
姬十三
守望轩
小花@BlogBus
林达华
浦宇平
白鸦
程化
罗浩|Startup Game
阮一峰
霍炬
飞之鸿
高远
鲍盛
机器学习/数据挖掘/信息检索/自然语言处理/认知科学/人工智能
AAAI
Apex
arXiv
Charles Kemp
Christopher Bishop
Christopher Manning
Cognitive Daily
Dan Jurafsky
David MacKay
ECML PKDD
Geoffrey Hinton
Herbert Simon
ICML
IJCAI
Jeff Hawkins
Jiawei Han
JMLR
Josh Tenenbaum
Larry Wasserman
Lucene
Marvin Minsky
MIT AI Lab
MIT Computational Cognitive Science Group
Mitchell Marcus
ML
NetLab
NIPS
Peter Norvig
Stanford AI Lab
Stanford NLP Lab
Stephen Boyd
Tom Mitchell
Trends in Cognitive Science
Vladimir Vapnik
Weka
Zhihua Zhou
技术
Coding Horror
High Scalability
Reddit
Stack Overflow
Steve Yegge
代码发芽网
淘宝UED团队
淘宝数据仓库团队
玩聚网
移山之道
其它
Gigapedia
Scientific American
Scientific American Mind
科学松鼠会
科幻世界
认识的朋友们
alai
chenyufei
dd
DreamHead
Googol
Jawley
Joyfire
littlestone
lxwde
Matrix67
realfun
RiceBall
roofalison
soloist
Tinyfool
windstorm
YongSun
书剑
云风
余晟
元凯宁
冯大辉(Fenng)
刘新宇
刘江@图灵
史晓明
吴新雨
周星星
周筠@博文视点
孟岩
张志强|阅微堂
张振
徐宥|4G Spaces
方舟@博文视点
曾登高
李笑来|Pure Pleasure
杨军
杨文博
熊节
王信文
王康生
苏杰@阿里巴巴
范怀宇
荣耀
莫华枫
蒋涛
袁泳(g9)|负暄琐话
许式伟
谢东升
谷文栋|Beyond Search
邹欣@MSRA
郑昀
阿朱
陈冀康@华章
陈怀兴
鲍志云
存档

翻译  《C++ Template Metaprogramming》第三章——深度探索元函数 收藏

C++ Template Metaprogramming

第三章:深度探索元函数

 

By David Abraham

(http://www.boost.org/people/dave_abrahams.htm)

 

By 刘未鹏(pongba)

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

 

原文链接(http://www.boost-consulting.com/mplbook)

 

C++模板元编程是C++最高阶最抽象也是最强大的能力,也是最前卫的技术,其带来的复用性以及代码强大的表达能力令人瞠目结舌,本章深入讨论C++元编程的机理以及应用,展现C++中最高阶的语义...

 

有了前面的基础知识作铺垫,我们来考察模板元编程技术的一个最基本的应用——为传统的不进行类型检查的操作添加静态类型检查。为此,我们将考察一个有关工程科学的实例——几乎在所有涉及科学计算的代码中都可以找到它的应用。在考察该例子的过程中,你将会学到一些重要的新的concepts,并且尝试使用MPL[1]Metaprogramming Library)进行高阶的模板元编程。

 

3.1 单位[2]分析

物理计算的首要原则是:数值并非是独立的——大多数物理量都有单位。而我们一不小心就会将单位置之脑后,这是件很危险的事情。随着计算变得越来越复杂,维持物理量的正确单位能够避免诸如将质量赋给长度将加速度和速度相加之类不经意间就会犯下的错误。这意味着为数值建立一个类型系统。

 

手动检查类型是件单调而乏味的工作,并且容易导致错误。当人们感到厌烦时,注意力就会分散,从而容易犯错误。然而,类型检查不正是计算机擅长的工作吗?如果我们能够为物理量和单位构建一个C++型别的framework,那么我们从公式中就可以捕获错误,而不用等到它们在现实世界中导致问题的时候。

 

阻止单位不同的物理量互操作并不难——我们可以简单地用类来表现单位,并且只允许相同的类(单位)互操作。但是问题远不止这么简单,不同的单位可以通过乘或除结合起来,从而产生一个复杂的新单位,由于可以不断乘除,所以产生的新单位其复杂度几乎是任意的。看来问题变得更有趣了!例如,牛顿定律(它将力,质量,加速度三者联系起来):

 

F=ma

 

由于质量和加速度有着不同的单位,所以力的单位必须是两者的结合。事实上,加速度的单位就已经是个混合物——单位时间内速度的改变:

 

dv/dt

 

又因为速度即单位时间内经过的距离,所以加速度的基本单位是:

 

(l/t)/t=l/t2

 

并且,加速度通常以米每平方秒来衡量。所以,力的单位为:

 

ml/t2

 

也就说,力通常以kg(m/s2)千克米每平方秒来衡量。当我们将质量和加速度相乘时,我们除了将数量相乘之外还必须将单位相乘,这可以帮我们确信结果是有意义的。这种(对单位的)簿记的正式名称为单位分析,而我们的下一个任务就是在C++类型系统中实现它。John BartonLee Nackman在它们的著作《Scientific and Engineering C++》中第一次展示了如何实现它。我们将沿袭他们的思路,只不过重新以元编程的方式来实现。

 

3.1.1 单位的表示

国际标准单位制规定了物理量的标准单位为:质量(kg),长度或位置(m),时间(s),电荷(c),温度(oc),密度(kg/m3),角度(o)。为了通用一些,我们的系统必须可以表示七个或七个以上的基本单位,还要能够表示复合单位,比如kg(m/s2))的单位这种经过几个基本单位乘除而成的复合单位。

 

一般来说,一个复合单位可以看成若干基本单位的幂的乘积[3]。如果要表示这些幂次以便在运行期可以操纵它们,我们可以使用一个数组,其七个元素每个对应一个不同的单位,而其值表示对应单位的幂次:

 

typedef int dimension[7]; //m l t ...

dimension const mass    ={1,0,0,0,0,0,0};

dimension const length  ={0,1,0,0,0,0,0};

dimension const time    ={0,0,1,0,0,0,0};

...

 

根据这种表示法,力的表示如下:

 

dimension const force = {1,1,-2,0,0,0};

 

也就是说,mlt-2。然而,如果我们想要将单位融入到类型系统[4]中去,这些数组就无法胜任了:它们的类型全都相同,都是dimension!而我们需要的是自身能够表示数值序列的类型,这样质量和长度的类型就是不同的,而两个质量的类型则是相同的。

 

幸运的是,MPL提供了一组表示类型序列的设施。例如,我们可以构建一个有符号整型的序列:

 

#include <boost/mpl/vector.hpp>

 

typedef boost::mpl::vector<

signed char, short, int, long> signed_types;

 

那么,我们如何用类型序列来表示单位[5]呢?由于数值型的元函数传递和返回的类型是具有内嵌::value的外覆类,所以数值序列其实是外覆类型的序列(另一个多态的例子)。为了使事情变得更为简单,MPL提供了int_<N>类模板,它以一个内嵌的::value来表现它的整型参数N

 

#include <boost/mpl/int.hpp>

 

namespace mpl = boost::mpl;[6] // namespace alias

static int const five = mpl::int_<5>::value;

 

事实上,MPL库包含了一整套整型常量的外覆类,如long_bool_等,每个外覆类对应一个不同类型的整型常量。

 

现在,我们可以将基本单位构建如下:

 

typedef mpl::vector<

mpl::int_<1>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

> mass;

 

typedef mpl::vector<

mpl::int_<0>, mpl::int_<1>, mpl::int_<0>, mpl::int_<0>

 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>

> length;

...

 

...你很快就会觉得这写起来实在太累人。更糟糕的是,这样的代码难于阅读和验证。代码的本质信息,也就是每个基本单位的幂次,被埋在重复的语法噪音中。因此,MPL相应还提供了整型序列外覆类,它允许我们写出类似下面的代码:

 

#include <boost/mpl/vector_c.hpp>

 

typedef mpl::vector_c<int,1,0,0,0,0,0,0> mass;

typedef mpl::vector_c<int,0,1,0,0,0,0,0> length; // or position

typedef mpl::vector_c<int,0,0,1,0,0,0,0> time;

typedef mpl::vector_c<int,0,0,0,1,0,0,0> charge;

typedef mpl::vector_c<int,0,0,0,0,1,0,0> temperature;

typedef mpl::vector_c<int,0,0,0,0,0,1,0> intensity;

typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;

 

你可以将这个特殊的mpl::vector_c看作与前面那个冗长的mpl::vector一样,尽管它们的类型并不相同。

 

如果我们愿意,我们还可以定义一些复合单位:

 

// 基本单位:m l t ...

typedef mpl::vector_c<int,0,1,-1,0,0,0,0> velocity; // l/t

typedef mpl::vector_c<int,0,1,-2,0,0,0,0> acceleration;

// l/(t2)

typedef mpl::vector_c<int,1,1,-1,0,0,0,0> momentum; // ml/t

typedef mpl::vector_c<int,1,1,-2,0,0,0,0> force; // ml/(t2)

 

并且,有时候,标量的单位(如pi,标量的单位即没有单位——译注)也可以这样来描述:

 

typedef mpl::vector_c<int,0,0,0,0,0,0,0> scalar;

 

3.1.2 物理量的表示

上面所列的类型仍然是纯粹的元数据。要想对真实的计算进行类型检查,我们还需要以某种方式将它们(元数据)绑定到运行时数据。一个简单的数值外覆类——模板参数为数据类型TT的单位——刚好合适:

 

template <class T, class Dimensions>

struct quantity

{

explicit quantity(T x)

: m_value(x)

{}

 

T value() const { return m_value; }

private:

T m_value;

};

 

现在,我们有了将数值和单位联系到一起的办法。例如,我们可以说:

 

quantity<float,length> l(1.0f);

quantity<float,mass> m(2.0f);

 

注意到在quantity的类定义体中并没有出现Dimensions模板参数的任何身影,它只在模板参数列表中出现过,其唯一作用是确保lm具有不同的类型。这样,我们就不可能错误地将长度赋给质量:

 

m = l; // 编译期错误

 

3.1.3 实现加法和减法

因为参数的类型(单位)必须总是匹配,所以我们现在可以轻易的写出加法和减法的规则:

 

template <class T, class D>

quantity<T,D>

operator+(quantity<T,D> x, quantity<T,D> y)

{

  return quantity<T,D>(x.value() + y.value());

}

 

template <class T, class D>

quantity<T,D>

operator-(quantity<T,D> x, quantity<T,D> y)

{

  return quantity<T,D>(x.value() - y.value());

}

 

这样,我们就可以写出类似下面的代码:

 

quantity<float,length> len1(1.0f);

quantity<float,length> len2(2.0f);

len1 = len1 + len2; //ok

 

并且,我们不能将不同单位的量相加:

 

len1 = len1 = quantity<float, mass>(3.7f); // error

 

3.1.4 实现乘法

乘法比加减法复杂一些。到目前为止,运算的参数和结果的单位都是一样的,但是做乘法时,结果的单位往往和两个参数的单位都不相同。对于乘法,下面的式子:

 

(Xa)(Xb) == X(a+b)

 

意味着结果的单位的指数为相应参数的单位的指数和。商与此类似,为指数差。

 

为此,我们使用MPLtransform算法来将两个序列中的对应元素相加。transform是个元函数,它遍历两个并行的输入序列,对于每个位置将两个序列中的对应元素传给一个任意的(用户提供的)二元元函数,并且将结果存入一个输出序列。

 

template <

class Sequence1,

class Sequence2,

class BinaryOperation

> 

struct transform; // return a sequence

 

如果你熟悉STLtransform算法的话,上面的struct transform的形式对于你可能并不陌生,STLtransform算法接受两个运行期的输入序列:

 

template <

class InputIterator1, class InputIterator2

, class OutputIterator, class BinaryOperation

> 

void transform(

InputIterator1 start1, InputIterator2 finish1

 , InputIterator2 start2

 , OutputIterator result, BinaryOperation func);

 

现在我们只需要向mpl::transform传递一个用于对单位进行乘除法(通过对两个序列的对应元素相加减)的BinaryOperation。如果你查看MPL的参考手册,你会看到plusminus两个元函数刚好可以满足要求:

 

#include <boost/static_assert.hpp>

#include <boost/mpl/plus.hpp>

#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl;

 

BOOST_STATIC_ASSERT((

mpl::plus<

mpl::int_<2>

, mpl::int_<3>

>::type::value == 5

));

 

BOOST_STATIC_ASSERT是一个宏,如果其参数为false,则会导致一个编译期错误。双括号是必要的,因为C++预处理器不能解析模板:如果不多加一对括号,那么它会将隔开模板参数的逗号当成隔开宏参数的逗号,从而将条件表达式错误地解析为若干宏参数。这和运行期的assert(...)不一样(后者是由C++编译期解析的,可以识别一切表达式——译注),BOOST_STATIC_ASSERT也可以用于类的定义域中,从而允许我们将其置于元函数中。第8章对此有更深入的讨论。

 

到目前为止,看起来我们已经有了一个解决方案,像这样:

 

#include <boost/mpl/transform.hpp>

 

template <class T, class D1, class D2>

quantity<

T

, typename mpl::transform<D1,D2,mpl::plus>::type

> 

operator*(quantity<T,D1> x, quantity<T,D2> y) { ... }

 

但是很抱歉,这还不够!现在如果你试图使用这个operator*,你会得到一个编译错误,原因就在于你将mpl::plus直接传给了mpl::transform,而(MPL)规定却说元函数的参数必须是类型,但mpl::plus却不是类型,而是一个类模板。所以我们必须通过某种方式让类似plus这样的元函数满足这种元数据(metadata)模型。

 

从某种意义上说,这就要求在元函数和元数据之间引入多态,一个很自然的途径是使用外覆类惯用手法——在前面的代码中,这种惯用手法曾在类型和整型常量之间引入了多态。而现在,我们将一个类模板内嵌于一个所谓的元函数类[7]中:

 

struct plus_f

{

template <class T1, class T2>

struct apply

{

typedef typename mpl::plus<T1,T2>::type type;

};

};

 

定义:元函数类是指内嵌有名为applypublic 元函数的类。

 

虽然元函数是模板而非类型,但是元函数类却以一个普通的非模板类将其包覆起来,使其成为一个类型。因为元函数操作和返回的都是类型,所以元函数类也可以被作为参数传递给另一个元函数,而元函数也可以返回一个元函数类。

 

从而,我们得到了一个plus_f元函数类,将它作为BinaryOperation传递给mpl::transform不会导致编译错误:

 

template <class T, class D1, class D2>

quantity<

T

 , typename mpl::transform<D1,D2,plus_f>::type //new dimensions

> 

operator*(quantity<T,D1> x, quantity<T,D2> y)

{

typedef typename mpl::transform<D1,D2,plus_f>::type dim;

return quantity<T,dim>( x.value() * y.value() );

}

 

现在,如果我们计算一个5公斤的膝上型计算机的重力,也就是说,将重力加速度乘以质量:

 

quantity<float,mass> m(5.0f);

quantity<float,acceleration> a(9.8f);

std::cout << "force = " << (m * a).value();

 

我们自定义的operator*会将这些运行期的值相乘(结果为49f),而我们的元程序代码则会通过transform将表现基本单位的元序列进行指数相加,所以结果类型为一个新的单位,其表示像这样:

 

mpl::vector_c<int,1,1,-2,0,0,0,0> // kgms-2

 

然而,如果我们试图写:

 

quantity<float,force> f = m*a;

 

我们会遇到一点问题。尽管m*a的结果的确表示:质量,长度,时间的指数分别为1,1,-2,然而transform返回的类型却并非vector_c。相反,transform处理它的输入元素,并以恰当的元素创建一个新的序列:这个新序列和mpl::vector_c<int,1,1,-2,0,0,0,0>具有几乎相同的属性,但它们却是完全不同的C++类型。如果你想要知道新序列的全名,你可以尝试编译这个例子,然后查看错误信息,但是确切的细节并不重要。关键的问题是force的类型和新序列的类型不同,所以赋值会失败。

 

为了解决这个问题,我们可以添加一个从乘法的结果类型到quantity<float,force>的隐式转换。由于我们无法预测介入计算的单位的确切类型(从而也就无法预测计算的结果的单位——译注),所以这个转换必须为模板形式的,像这样:

 

template <class T, class Dimensions>

struct quantity

{

// converting constructor

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs)

: m_value(rhs.value())

{

}

...

 

然而,很不幸的是,这样一个通用的转换彻底违背了我们原来的意图,一旦有了这个转换,我们就可以写出下面的代码:

 

// m*a的结果应该是力(force),而非质量(mass)

quantity<float,mass> bogus = m * a;

 

这简直糟透了!

 

幸运的是,我们可以通过另一个MPL算法——equal——来解决这个问题,equal用于测试两个序列是否具有相同的一集元素:

 

template <class OtherDimensions>

quantity(quantity<T,OtherDimensions> const& rhs)

  : m_value(rhs.value())

{

BOOST_STATIC_ASSERT((

     mpl::equal<Dimensions,OtherDimensions>::type::value

  ));

}

 

现在,如果两个单位不匹配,那么这个assertion就会导致一个编译错误,及时阻止你的错误行为。

 

3.1.5 实现除法

除法和乘法类似,乘法将指数相加,而除法将指数相减。显然,作除法的元函数类minus_f完全可以按照plus_f的形式来写,但这里我们将使用一个新的技巧来进一步简化minus_f元函数类:

 

struct minus_f

{

template <class T1, class T2>

  struct apply

: mpl::minus<T1,T2> {};

};

 

这里,minus_f::apply使用了继承来将其基类mpl::minus“type”内嵌类型暴露出来。这样我们就不必写:

 

typedef typename ...::type type

 

这个强有力的简化代码的手法被称为元函数转发。后面我们还会频繁使用它。注意,我们不用在apply的基类mpl::minus<T1,T2>前面加上typename(加了反而会错),因为编译器知道apply的基类列表中只可能有类型。

 

尽管有这样的语法技巧来简化代码,但一遍遍地写这些简单之极的外覆类仍然会很快让人感到厌烦。虽然minus_f没有plus_f那么臃肿,但你仍要为此写一堆代码。幸运的是,MPL为我们提供了简单得多的办法,我们用不着写一整个的元函数类(如minus_f),而是可以直接将元函数传给算法,例如,我们可以这样调用mpl::transform

 

typename mpl::transform<D1,D2,mpl::minus<_1, _2> >::type

 

其中有两个看起来很奇怪的参数(_1_2),它们被称为占位符,这里它们的意思是:当transformBinaryOperation被调用时,其第一第二个参数会被相应地传递到minus_1_2处。而mpl::minus<_1,_2>则被称为占位符表达式

 

附注MPL的占位符位于mpl::placeholders名字空间内,定义在boost/mpl/placeholder.hpp文件中。在本书中,我们会假定你已经写了如下代码:

#include<boost/mpl/placeholder.hpp>

using namespace mpl::placeholders;

这样,像_1,_2这样的占位符才能够不加名字空间限定的访问。

 

使用占位符后的operator / 像这样:

 

template <class T, class D1, class D2>

quantity<

T

 , typename mpl::transform<D1,D2,mpl::minus<_1,_2> >::type

> 

operator / (quantity<T,D1> x, quantity<T,D2> y)

{

typedef typename

mpl::transform<D1,D2,mpl::minus<_1,_2> >::type dim;

 

return quantity<T,dim>( x.value() / y.value() );

}

 

代码明显变得更为简洁了(因为用不着额外定义一个minus_f类)。我们还可以将计算新单位的代码分解到一个新的元函数中,这样代码将继续得到简化:

 

template <class D1, class D2>

struct divide_dimensions

: mpl::transform<D1,D2,mpl::minus<_1,_2> > //再次转发

{};

 

template <class T, class D1, class D2>

quantity<T, typename divide_dimensions<D1,D2>::type>

operator/(quantity<T,D1> x, quantity<T,D2> y)

{

return quantity<T, typename divide_dimensions<D1,D2>::type>(

x.value() / y.value());

}

 

现在我们可以验证膝上型计算机的重力是否计算正确,通过一个逆向的计算,我们得到其质量,然后将它与条件给出的计算机质量比较:

 

quantity<float,mass> m2 = f/a;

float rounding_error = std::abs((m2-m).value());

 

如果一切正常,那么rounding_error会非常接近0。这些计算虽令人厌烦,但是如果它们出错则往往会破坏整个程序(甚至更糟)。如果我们将f/a错写成了a/f,我们会得到一个编译错误,及时防止错误在整个程序中蔓延。

 

3.2 高阶元函数(Higher-Order Metafunctions

 

在前面一节,我们传递或返回元函数时使用了两种格式——元函数类和占位符表达式。通过把元函数塞进第一流的(first class)的元数据中,能够允许transform执行各种不同的操作,例如,上面例子中的单位乘除。尽管使用函数去操纵其它函数的思想可能看起来比较简单,然而却具有非常强大的能力和灵活性,因此赢得了一个好听的名字:高阶函数式编程(higher-order functional programming。操纵其它函数的函数被称为高阶函数。所以,transform是个高阶元函数:操纵其它元函数的元函数。

 

现在我们已经见识过了高阶元函数的强大能力,下面我们将尝试创建新的高阶元函数。为了探究其底层机理,让我们先来看一个简单的例子。我们的任务是写一个名为twice的元函数,twice满足下面的条件:给它一个一元元函数f以及任意的元数据x,它将作如下的计算:

 

twice(f,x) := f(f(x))

 

这个例子看起来没什么价值——它的确没有。你大概不会在实际编码中使用twice。但使用它并非我们的目的,twice包含了一个高阶元函数的所有必要元素,并且没有会令你分散注意力的其它细节,尽管它只是接受并调用一个元函数。

 

如果f是个元函数类,那么twice的定义会很直观:

 

template <class F, class X>

struct twice

{

  typedef typename F::template apply<X>::type once; //f(x)

  typedef typename F::template apply<once>::type type;//f(f(x))

};

 

或者使用元函数转发:

 

template <class F, class X>

struct twice

: F::template apply<

     typename F::template apply<X>::type

> 

{};

 

C++语言.附注

C++标准要求:当我们使用依赖名字(dependent name并且该名字指的是一个成员模板时,我们必须使用template关键字。F::apply不一定指的是个模板名字,其含义依赖F。而F::template apply则确切的告诉编译器apply(应该)是个成员模板。关于template,附录B有更多信息。

 

显然,在每次使用元函数类的时候都在apply前加上template关键字是个负担,通过将这种使用模式分解到一个元函数中,我们可以减轻这个负担:

 

template <class UnaryMetaFunctionClass, class Arg>

struct apply1

  : UnaryMetaFunctionClass::template apply<Arg>

{};

 

现在,twice可以简化成这样:

 

template <class F, class X>

struct twice

: apply1<F, typename apply1<F,X>::type>

{};

 

我们来看一下twice的使用——将它应用到add_pointer_f元函数类上:

 

struct add_pointer_f

{

template <class T>

  struct apply : boost::add_pointer<T> {};

};

BOOST_STATIC_ASSERT((

  boost::is_same<

    twice<add_pointer_f, int>::type

, int**

>::value

));

 

我们可以看出,将twiceadd_pointer_f一起使用可以创建指针的指针

 

3.3 处理占位符

虽然我们的twice实现已经可以与元函数类一起工作了,但理想情况下,我们还要求它能够与占位符表达式一起工作,就像transform允许我们传递两种形式的元函数一样。例如,我们得能够写出这样的代码:

 

template <class X>

struct two_pointers

: twice<boost::add_pointer<_1>, X>

{};

 

但是我们只要考察一下boost::add_pointer的实现就会发现,目前的twice根本不能这样工作:

 

template<class T>

struct add_pointer

{

typedef T* type;

}

 

boost::add_pointer<_1>必须是个元函数类(就像add_pointer_f那样),才能够被twice调用。然而事实上它却是一个无参(nullary)元函数,返回几乎毫无意义的_1*类型。所有试图使用two_pointers的地方都会失败,因为当apply1要求boost::add_pointer<_1>内嵌的::apply元函数时会发现其根本不存在。

 

我们并没有得到想要的行为。下面该怎么办呢?想想看,既然mpl::transform可以做到,那么我们应该也有办法做到——下面就是:

 

3.3.1 lambda元函数

我们可以使用MPLlambda元函数,由boost::add_pointer<_1>生成一个元函数类:

 

template <class X>

struct two_pointers

: twice<typename mpl::lambda<boost::add_pointer<_1> >::type, X>

{};

 

BOOST_STATIC_ASSERT((

  boost::is_same<

typename two_pointers<int>::type

, int**

>::value

));

 

后面我们将把add_pointer_f这样的元函数类或boost::add_pointer<_1>这样的占位符表达式统称lambda表达式。这个称呼的含义是匿名(unnamed)函数对象,它是在二十世纪三十年代由逻辑学家Alonzo Church引入的,作为被他称为lambda计算lambda-calculus[8])的计算理论中的一部分。之所以使用lambda这个含义有点晦涩的名词是由于它在函数式编程语言中建立的良好先例。

 

尽管mpl::lambda的主要意图是将占位符表达式转化为元函数类,然而它也可以接受任何lambda表达式,即使该表达式已经是个元函数类。在后一种情况,mpl::lambda原样返回其参数。MPL算法(如transform)在内部使用了mpl::lambda,然后再调用其返回(生成)的元函数类,所以它们和两种lambda表达式都相处得不错。我们可以将相同的策略应用到twice上:

 

template <class F, class X>

struct twice

: apply1<

typename mpl::lambda<F>::type

, typename apply1<

       typename mpl::lambda<F>::type

      , X

>::type

> 

{};

 

现在我们可以将twice和元函数类或占位符表达式一起使用了:

 

int* x;

 

twice<add_pointer_f, int>::type          p = &x;

twice<boost::add_pointer<_1>, int>::type q = &x;

 

3.3.2 apply元函数

调用lambda返回的元函数类在MPL中是极为常见的模式,以至于MPL提供了一个apply元函数来做这件事。使用mpl::apply,我们的twice会变得更加灵活:

 

#include <boost/mpl/apply.hpp>

 

template <class F, class X>

struct twice

: mpl::apply<F, typename mpl::apply<F,X>::type>

{};

 

你可以将mpl::apply看作与apply1相同,不过apply有另外两个特性:

 

1. apply1只能操作元函数类,而mpl::apply的第一个参数可以是任意的lambda表达式(包括占位符表达式)[9]

2. apply1只能接受除元函数类之外的1个额外参数,并将这个参数传给元函数类。而mpl::apply可以接受15个额外的参数[10],并用它们来调用元函数类。例如:

 

//将二元的lambda表达式应用到另外两个参数上

mpl::apply<

mpl::plus<_1,_2>

, mpl::int_<6>

, mpl::int_<7>

>::type::value // == 13

 

原则

如果你要在你的元函数中调用其某个参数(即:将某个参数作为元函数类来调用——译注),请使用mpl::apply以确保该调用对于两种lambda表达式皆是有效的。

 

3.4 lambda的其它能力

lambda表达式的能力并不止于使元函数成为可传递的参数。下面介绍的另外两种能力使lambda表达式成为几乎每个元编程任务中不可或缺的部分。

 

3.4.1 部分函数应用(Partial Metafunction Application

考虑lambda表达式mpl::plus<_1,_1> :单个的参数会被传递到plus的两个“_1”的位置,也就是说,将一个值与自身相加。因此,这里,一个二元的元函数被用来创建了一个一元lambda表达式。换句话说,我们创建了一个全新的运算(plus原先是做加法运算的,但plus<_1,_1>却是将一个值与自身相加,也就是2”运算——译注)!然而,还不止这些,通过将一个普通类型(非占位符)绑定到plus的其中一个参数,我们可以创建一个一元lambda表达式,其作用为将它的参数加上一个定值(如42):

 

mpl::plus<_1,mpl::int_<42> >

 

将一集实参绑定到某个函数的形参的一个子集的过程在函数式编程语言[11]中被称为部分函数应用

 

3.4.2 复合元函数[12]Metafunction Composition

lambda表达式也可以被用于组合简单的元函数以产生更为有趣的运算。例如,下面的表达式将两个数的和与差相乘(即(a+b)*(a-b)——译注):

 

mpl::multiplies<mpl::plus<_1,_2>, mpl::minus<_1,_2> >

 

可以看出,它是三个元函数(multiplies,plus,minus)的复合体。

 

当对一个lambda表达式求值时,MPL会先检查它的各个参数以确定它们自身是否lambda表达式[13],如果是,则先将它们求值,并将这些(本身为lambda表达式的)参数替换为求值的结果,然后再对外围的lambda表达式求值[14]

 

3.5 Lambda的细节

现在你对MPLlambda设施的语义应该有了一个大致的了解,既然如此,让我们将前面的理解形式化(正式化),并且考察一些更为深入的东西。

 

3.5.1 占位符

占位符的定义可能会吓你一跳:

 

定义

占位符是一个形为mpl::arg<N>的元函数。

 

3.5.1.1 实现(Implementation

_1_2_3这些名字只不过是为了方便起见,其实它们是mpl::arg的特化版本的typedefsmpl::arg<N>作为元函数的作用是选出(并返回)它的第N个参数[15]。占位符的实现像这样:

 

namespace boost {

namespace mpl {

namespace placeholders {

 

template <int N> struct arg; // 前导声明

struct void_;

 

template <>

struct arg<1>

{

template <

class A1, class A2 = void_, ... class Am = void_>

struct apply

{

typedef A1 type; // 返回其第一个参数

};

};

 

typedef arg<1> _1;

 

template <>

struct arg<2>

{

template <

class A1, class A2, class A3 = void_, ...class Am = void_

> 

struct apply

{

typedef A2 type; //返回其第二个参数

};

};

 

typedef arg<2> _2;

 

//其它特化版本和typedefs...

 

}}}

 

前面说过,调用元函数类就是调用其内嵌的::apply元函数。当一个lambda表达式中的某个占位符被求值时,其实就是以该lambda表达式的实际参数来调用该占位符,然后该占位符会返回参数中的某一个[16]。再然后求值(返回)的结果会替换lambda表达式中该占位符所的位置。如此重复,直到所有的占位符都被替换成它们所表示的(实际的)参数。

 

3.5.1.2 匿名(Unnamed)占位符

匿名占位符是个非常特殊的占位符,其定义如下:

 

namespace boost { namespace mpl { namespace placeholders {

 

typedef arg<-1> _; //匿名占位符

 

}}}

 

其实现细节并不重要。对于匿名占位符,你所需知道的就是:它是被特殊对待的。当一个lambda表达式被mpl::lambda转化为元函数类时,在某个给定的模板特化体中的N个出现的匿名占位符会被替换为_N

 

例如,下面的表3.1中的每一行都包含两个等价的lambda表达式:

 

                              3.1

     mpl::plus<_,_>

     mpl::plus<_1,_2>

     boost::is_same<

          _

         ,boost::add_pointer<_>

     >

     boost::is_same<

          _1

         ,boost::add_pointer<_1>

     >

     mpl::multiplies<

         mpl::plus<_,_>

        ,mpl::minus<_,_>

     >

     mpl::multiplies<

         mpl::plus<_1,_2>

        ,mpl::minus<_1,_2>

     >

 

3.5.2 占位符表达式的定义

现在你应该已经知道了占位符的含义了。既然如此,我们可以定义占位符表达式如下:

 

定义

一个占位符表达式是:

一个占位符

或者

一个其参数至少有一个为占位符表达式的模板特化体。

 

换句话说,一个占位符表达式始终包含(至少)一个占位符。

 

3.5.3 lambda和非元函数(Non-Metafunction)模板

关于占位符表达式,一个尚未讨论的细节是:为了使普通模板更容易融入元编程,MPL对它们使用了特殊的规则。在所有的占位符都被相应的实际参数替换后,如果作为结果的模板特化体X并没有内嵌的::type,那么结果就是X自身。

 

例如,mpl::apply<std::vector<_>,T>的结果始终都是std::vector<T>。如果不是由于这个行为,我们就得写一个元函数用于在lambda表达式中创建模板特化体:

 

// trivial std::vector generator

template<class U>

struct make_vector { typedef std::vector<U> type; };

 

typedef mpl::apply<make_vector<_>, T>::type vector_of_t;

 

但是现在由于有了这个特殊规则,我们可以简单地写:

 

typedef mpl::apply<std::vector<_>, T>::type vector_of_t;

 

3.5.4懒惰的重要性

回顾上一章提到的always_int

 

struct always_int

{

typedef int type;

};

 

无参(nullary)元函数可能看起来并不重要,因为像add_pointer<int>这样的类型在任何lambda表达式中出现的地方都可以被替换为int*。但并非所有的无参元函数都像这样简单!例如:

 

struct add_pointer_f

{

template <class T>

  struct apply : boost::add_pointer<T> {};

};

typedef mpl::vector<int, char*, double&> seq;

typedef mpl::transform<seq, add_pointer_f> calc_ptr_seq;

 

注意到calc_ptr_seq是个无参元函数,因为它有transform的内嵌::type。但是,对于一个C++模板,只有当我们试图观察其内部时,它才会被实例化。仅仅将calc_ptr_seq作为一个typedef名字并不会导致它被求值,因为我们并没有访问它内部的::type

 

元函数接受了它的参数后仍可以被延迟调用。当一个元函数只是被选择性的使用时,我们可以使用惰性求值[17]lazy evaluation来减少编译时间。有时,通过命名[18]一个无效的计算而并不去实际执行它,我们还可以避免扭曲程序结构[19]。我们对calc_ptr_seq正是这么做的,因为double&*是非法类型。这种懒性和它的优点是本书中将会重复出现的主题。

 

3.6 细节

 

到目前为止,你对一般的模板元编程和BoostMPL库的基本概念和语言应该有了一个相当全面的了解。本节回顾其中的要点。

 

元函数转发(Metafunction forwarding

使用public继承将元函数中内嵌的::type暴露给用户的技术[20]

 

元函数类(Metafunction class

将编译期函数形式化的最基本方法,由此,编译期函数可以被看作多态的元数据,也就是看作一个类型。元函数类是个内嵌有名为apply的元函数的类。

 

MPL

本书中的大部分例子都用到了Boost Metaprogramming Library(即MPL)。正如Boosttype traits的头文件一样,MPL头文件遵循一个简单的约定:

 

#include <boost/mpl/component-name.hpp>

 

然而,如果MPL的某个组件名以下划线结尾,那么对应的MPL头文件名就不包含最后的下划线。例如,mpl::bool_可以在<boost/mpl/bool.hpp>中找到。如果该库的哪些地方没有遵循这个约定,我们会为你指出来。

 

高阶函数(Higher-order function

操作或返回函数的函数。利用其它元数据使元函数成为多态的是高阶元编程中的一个关键之处。

 

lambda表达式

简单的说,lambda表达式是可以被调用的元数据。如果没有可调用元数据的某些形式,高阶元函数也不会成为可能。lambda表达式有两个基本形式:元函数类占位符表达式

 

占位符表达式

lambda表达式的一种。通过使用占位符达到部分函数应用复合元函数的目的。正如你将会在本书中随处可见的,这些特性给予我们惊人的能力,允许我们从原始的元函数构造出几乎任意复杂的类型计算——就在它被使用之处:

 

// find the position of a type x in some_sequence such that:

//         x is convertible to 'int'

//      && x is not 'char'

//      && x is not a floating type

typedef mpl::find_if<

some_sequence

, mpl::and_<

    boost::is_convertible<_1,int>

   , mpl::not_<boost::is_same<_1,char> >

   , mpl::not_<boost::is_float<_1> >

  >

>::type iter;

 

占位符表达式使我们不必(为元函数)写新的(外覆)元函数类,实现了算法复用的目的。而这种能力在STL的运行期世界里却严重缺乏,因为如果不论标准算法的正确性和效率,则手写一个循环往往比使用标准算法简单得多。

 

lambda元函数(The ‘lambda’ metafunction

lambda表达式转化为元函数类的元函数。要得到关于lambdalambda求值过程的更为详细的信息,请参考MPL的参考手册。

 

apply元函数(The ‘apply’ metafunction

一个元函数,其行为是:以其余的参数去调用其第一个参数,后者必须是个lambda表达式。通常,要调用一个lambda表达式,你应该总是将它以及调用它的参数传给mpl::apply,而不是手动使用mpl::lambda

 

惰性求值(Lazy evaluation

一种将计算推迟到其结果被要求的时候的策略。这种策略可以避免所有不必要的计算和不必要的错误。元函数仅仅在我们访问其内嵌的::type时才会被(真正)调用,所以我们可以在提供了其所有参数的同时却不作任何实质性的计算,而是将求值延迟到必要的时候。

 



[1] 译注:MPLBoost库里面的一个子库。用于支持模板元编程。下文会多次提到这个MPL库。

[2] 译注:这里原文为Dimensional Analysis,这里的Dimensional并非作通常意义上的维度解释。而是作为物理上的单位解释,因为下文讲的正是如何在编译期对物理量进行单位检查,进而实现一个编译期的健全的单位系统。Dimensional Analysis的正式称呼为“量纲分析”,太学术化,所以这里我们用通常物理上的称呼。

[3] 1/x看成x的-1次方。由此,m/s2可以写成ms-2,就由商的形式变成了积的形式。

[4] 译注:作者的意思是“让每个不同的单位成为不同的类型”。

[5] 译注:这里的原文是“...represent numbers”,直译为“...表示数值”,但这里的意思其实是表示数值的单位。

[6] namespace alias=namespace-name;alias声明为namespace-name的别名。在本书的许多例子中都会使用mpl::来表示boost::mpl::

[7] 译注:元函数本身是个类模板。而元函数类是个类型,它将元函数内嵌为一个名为apply的类模板,这两个称呼在后面将会多次提到,请读者注意它们的区别。

[9] 译注:不过似乎boost 1.31.0里面的mpl::apply并没有这个特性。或许是权衡后的考虑?

[10] MPL参考手册的Configuration Macros部分描述了如何改变mpl::apply能够接受的参数个数的上限。

[11] 译注:这里作者的原文是...in the world of functional programming...”,本该译为“...在函数式编程中...”,然而考虑到“函数式编程”可能会发生误导,而译为“在函数式编程语言中”则不会,因为后者是个被广泛使用的名词。

[12] 译注:这里还可以译为“元函数组合”“元函数合成”等,视composition的译法而定。但考虑到数学中的“复合函数”一说,所以这里译为“复合元函数”,“复合”可作动词,可作形容词。如果作动词则表示“将元函数复合起来”,这正是原文表达的意思,如果作形容词则表示“复合后的元函数”,这是“复合”的结果。这样似乎更好一些:)

[13] 译注:这里所说的lambda表达式的各个参数并非该lambda接受的“外界”参数,举个例子:mul<plus<_1,_2>,minus<_1,_2> >这个lambda表达式的参数就是 plus<_1,_2>minus<_1,_2>而它们各自又都是lambda表达式,所以它们会先被求值,然后将结果传给mul

[14] 译注:事实上,lambda表达式的求值是个递归的过程。

[15] MPL缺省提供了5个占位符。MPL参考手册的Configuration Macros部分有关于如何改变提供的占位符的数目的描述。

[16] 译注:如果该占位符为_N,那么就会返回实际参数中的第N个参数,占位符的“占位”的意思就是:_N“占”的是第N个参数的位置。

[17] 译注:lazy evaluation的意思是“不到必要时不求值”。

[18] 译注:这里,“命名(naming)”的意思是,仅仅给它一个名字(意味着“仅仅实例化它的名字”),而并不对该计算求值(意味着“并不实例化该类”)。

[19] 译注:一个不错的例子是apply_if,其详细介绍见boost的官方文档。

[20] 译注:这里的原文写得相当拗口,所以译文遵循前文的定义。含义一样。

发表于 @ 2004年09月01日 06:14:00 | 评论( loading... ) | 编辑| 举报| 收藏

旧一篇:C++标准库的一个有趣的小bug | 新一篇:《C++ Template Metaprogramming》译序——经典之后的经典

  • 发表评论
  • 评论内容:
  •  
Copyright © pongba
Powered by CSDN Blog