﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-奔向C++-随笔分类-C/C++学习笔记</title><link>http://www.cppblog.com/zhaoyg/category/7254.html</link><description>记录点滴，成就未来</description><language>zh-cn</language><lastBuildDate>Wed, 02 May 2012 23:31:02 GMT</lastBuildDate><pubDate>Wed, 02 May 2012 23:31:02 GMT</pubDate><ttl>60</ttl><item><title>我的无知与C++的变态</title><link>http://www.cppblog.com/zhaoyg/archive/2012/05/03/173536.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 02 May 2012 16:12:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2012/05/03/173536.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/173536.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2012/05/03/173536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/173536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/173536.html</trackback:ping><description><![CDATA[<span style="font-size: 12pt;">此刻我本已在床上睡觉了，但就在我睡前翻看《深度探索C++对象模型》时我看到了一段内容，也就是这段内容使得我写下了这篇笔记。</span><br /><br /><span style="font-size: 12pt;">这段内容大致讲的是关于模板中的命名决议：&#8220;scope of the template definition&#8221;与&#8220;scopy of the template instantition&#8221;</span><span style="font-size: 12pt;">。</span><br /><br /><span style="font-size: 12pt;">坦白的说我是第一次看到这一知识点，在上机验证后又回想《C++ Primer》 4th中可曾有提及过，后来发现4th中没有，倒是3rd中有专门的一小节专门陈述了下。<br /><br />后来我在《深度探索C++对象模型》书中关于模板中命名决议内容的旁边写下了这样一段话：<br /><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天才发现还有这么个东西。我只能承认自己的无知与C++的变态，面对这一知识点。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2012.5.2 夜<br /><br /><br /><br /></em></span><img src ="http://www.cppblog.com/zhaoyg/aggbug/173536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2012-05-03 00:12 <a href="http://www.cppblog.com/zhaoyg/archive/2012/05/03/173536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】C++11中值得关注的几大变化</title><link>http://www.cppblog.com/zhaoyg/archive/2011/08/19/153882.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Fri, 19 Aug 2011 13:06:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2011/08/19/153882.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/153882.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2011/08/19/153882.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/153882.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/153882.html</trackback:ping><description><![CDATA[<div><div><div>     <p>赖勇浩（http://laiyonghao.com）<br /> 声明：本文源自 Danny Kalev 在 2011 年 6 月 21 日发表的《The Biggest Changes in  C++11(and Why You Should Care)》一文，几乎所有内容都搬了过来，但不是全文照译，有困惑之处，请参详原文（<a href="http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/trackback/">http://www.softwarequalityconnection.com/2011/06/the-biggest-changes-in-c11-and-why-you-should-care/</a> ）。<br /> 注：作者 Danny Kalev 曾是 C++ 标准委员会成员。</p> <h2>Lambda 表达式</h2> <p>Lambda 表达式的形式是这样的：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>[capture](parameters)-&gt;return-type&nbsp;{body}&nbsp;&nbsp;</span></li></ol></div>  <br /> 来看个计数某个字符序列中有几个大写字母的例子：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>int&nbsp;main()&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;<span>char&nbsp;s[]="Hello&nbsp;World!";&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;<span>int&nbsp;Uppercase&nbsp;=&nbsp;0;&nbsp;//modified&nbsp;by&nbsp;the&nbsp;lambda&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;for_each(s,&nbsp;s+<span>sizeof(s),&nbsp;[&amp;Uppercase]&nbsp;(char&nbsp;c)&nbsp;{&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;<span>if&nbsp;(isupper(c))&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Uppercase++;&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;});&nbsp;&nbsp;</li><li>&nbsp;cout&lt;&lt;&nbsp;Uppercase&lt;&lt;<span>"&nbsp;uppercase&nbsp;letters&nbsp;in:&nbsp;"&lt;&lt;&nbsp;s&lt;&lt;endl;&nbsp;&nbsp;</span></li><li>}&nbsp;&nbsp;</li></ol></div>  <br /> 其中 [&amp;Uppercase] 中的 &amp; 的意义是 lambda 函数体要获取一个 Uppercase 引用，以便能够改变它的值，如果没有 &amp;，那就 Uppercase 将以传值的形式传递过去。 <h2>自动类型推导和 decltype</h2> <p>在 C++03 中，声明对象的同时必须指明其类型，其实大多数情况下，声明对象的同时也会包括一个初始值，C++11 在这种情况下就能够让你声明对象时不再指定类型了：<br /> </p><div bg_c-sharp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>auto&nbsp;x=0;&nbsp;//0&nbsp;是&nbsp;int&nbsp;类型，所以&nbsp;x&nbsp;也是&nbsp;int&nbsp;类型&nbsp;&nbsp;</span></li><li>auto&nbsp;c=<span>'a';&nbsp;//char&nbsp;&nbsp;</span></li><li>auto&nbsp;d=0.5;&nbsp;<span>//double&nbsp;&nbsp;</span></li><li>auto&nbsp;national_debt=14400000000000LL;<span>//long&nbsp;long&nbsp;&nbsp;</span></li></ol></div>  <br /> 这个特性在对象的类型很大很长的时候很有用，如：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>void&nbsp;func(const&nbsp;vector&lt;int&gt;&nbsp;&amp;vi)&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;&nbsp;vector&lt;<span>int&gt;::const_iterator&nbsp;ci=vi.begin();&nbsp;&nbsp;</span></li><li>}&nbsp;&nbsp;</li></ol></div>  <br /> 那个迭代器可以声明为：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>auto&nbsp;ci=vi.begin();&nbsp;&nbsp;</span></li></ol></div>  <br /> C++11 也提供了从对象或表达式中&#8220;俘获&#8221;类型的机制，新的操作符 decltype 可以从一个表达式中&#8220;俘获&#8221;其结果的类型并&#8220;返回&#8221;：<br /> <div bg_c-sharp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>const&nbsp;vector&lt;int&gt;&nbsp;vi;&nbsp;&nbsp;</span></li><li>typedef&nbsp;decltype&nbsp;(vi.begin())&nbsp;CIT;&nbsp;&nbsp;</li><li>CIT&nbsp;another_const_iterator;&nbsp;&nbsp;</li></ol></div>  <h2>统一的初始化语法</h2> <p>C++ 最少有 4 种不同的初始化形式，如括号内初始化，见：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>std::string&nbsp;s("hello");&nbsp;&nbsp;</span></li><li><span>int&nbsp;m=int();&nbsp;//default&nbsp;initialization&nbsp;&nbsp;</span></li></ol></div>  <br /> 还有等号形式的：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>std::string&nbsp;s="hello";&nbsp;&nbsp;</span></li><li><span>int&nbsp;x=5;&nbsp;&nbsp;</span></li></ol></div>  <br /> 对于 POD 集合，又可以用大括号：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>int&nbsp;arr[4]={0,1,2,3};&nbsp;&nbsp;</span></li><li><span>struct&nbsp;tm&nbsp;today={0};&nbsp;&nbsp;</span></li></ol></div>  <br /> 最后还有构造函数的成员初始化：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>struct&nbsp;S&nbsp;{&nbsp;&nbsp;</span></li><li>&nbsp;<span>int&nbsp;x;&nbsp;&nbsp;</span></li><li>&nbsp;S():&nbsp;x(0)&nbsp;{}&nbsp;};&nbsp;&nbsp;</li></ol></div>  <br /> 这么多初始化形式，不仅菜鸟会搞得很头大，高手也吃不消。更惨的是 C++03 中居然不能初始化 POD 数组的类成员，也不能在使用 new[] 的时候初始 POD 数组，操蛋啊！C++11 就用大括号一统天下了：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>class&nbsp;C&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li><span>int&nbsp;a;&nbsp;&nbsp;</span></li><li><span>int&nbsp;b;&nbsp;&nbsp;</span></li><li><span>public:&nbsp;&nbsp;</span></li><li>&nbsp;C(<span>int&nbsp;i,&nbsp;int&nbsp;j);&nbsp;&nbsp;</span></li><li>};&nbsp;&nbsp;</li><li>C&nbsp;c&nbsp;{0,0};&nbsp;<span>//C++11&nbsp;only.&nbsp;相当于&nbsp;C&nbsp;c(0,0);&nbsp;&nbsp;</span></li><li><span>int*&nbsp;a&nbsp;=&nbsp;new&nbsp;int[3]&nbsp;{&nbsp;1,&nbsp;2,&nbsp;0&nbsp;};&nbsp;/C++11&nbsp;only&nbsp;&nbsp;</span></li><li><span>class&nbsp;X&nbsp;{&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;<span>int&nbsp;a[4];&nbsp;&nbsp;</span></li><li><span>public:&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;X()&nbsp;:&nbsp;a{1,2,3,4}&nbsp;{}&nbsp;<span>//C++11,&nbsp;初始化数组成员&nbsp;&nbsp;</span></li><li>};&nbsp;&nbsp;</li></ol></div>  <br /> 还有一大好事就是对于容器来说，终于可以摆脱 push_back() 调用了，C++11中可以直观地初始化容器了：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>//&nbsp;C++11&nbsp;container&nbsp;initializer&nbsp;&nbsp;</span></li><li>vector&nbsp;vs&lt;string&gt;={&nbsp;<span>"first",&nbsp;"second",&nbsp;"third"};&nbsp;&nbsp;</span></li><li>map&nbsp;singers&nbsp;=&nbsp;&nbsp;</li><li>&nbsp;&nbsp;{&nbsp;{<span>"Lady&nbsp;Gaga",&nbsp;"+1&nbsp;(212)&nbsp;555-7890"},&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;{<span>"Beyonce&nbsp;Knowles",&nbsp;"+1&nbsp;(212)&nbsp;555-0987"}};&nbsp;&nbsp;</span></li></ol></div>  <br /> 而类中的数据成员初始化也得到了支持：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>class&nbsp;C&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;<span>int&nbsp;a=7;&nbsp;//C++11&nbsp;only&nbsp;&nbsp;</span></li><li><span>public:&nbsp;&nbsp;</span></li><li>&nbsp;C();&nbsp;&nbsp;</li><li>};&nbsp;&nbsp;</li></ol></div>  <h2>deleted 函数和 defaulted 函数</h2> <p>像以下形式的函数：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>struct&nbsp;A&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;A()=<span>default;&nbsp;//C++11&nbsp;&nbsp;</span></li><li>&nbsp;<span>virtual&nbsp;~A()=default;&nbsp;//C++11&nbsp;&nbsp;</span></li><li>};&nbsp;&nbsp;</li></ol></div>  <br /> 叫做 defaulted 函数，=default; 指示编译器生成该函数的默认实现。这有两个好处：一是让程序员轻松了，少敲键盘，二是有更好的性能。<br /> 与 defaulted 函数相对的就是 deleted 函数：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>int&nbsp;func()=delete;&nbsp;&nbsp;</span></li></ol></div>  <br /> 这货有一大用途就是实现 noncopyabe 防止对象拷贝，要想禁止拷贝，用 =deleted 声明一下两个关键的成员函数就可以了：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>struct&nbsp;NoCopy&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;&nbsp;&nbsp;&nbsp;NoCopy&nbsp;&amp;&nbsp;operator&nbsp;=(&nbsp;<span>const&nbsp;NoCopy&nbsp;&amp;&nbsp;)&nbsp;=&nbsp;delete;&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;&nbsp;&nbsp;NoCopy&nbsp;(&nbsp;<span>const&nbsp;NoCopy&nbsp;&amp;&nbsp;)&nbsp;=&nbsp;delete;&nbsp;&nbsp;</span></li><li>};&nbsp;&nbsp;</li><li>NoCopy&nbsp;a;&nbsp;&nbsp;</li><li>NoCopy&nbsp;b(a);&nbsp;<span>//编译错误，拷贝构造函数是&nbsp;deleted&nbsp;函数&nbsp;&nbsp;</span></li></ol></div>  <h2>nullptr</h2> <p>nullptr 是一个新的 C++ 关键字，它是空指针常量，它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>void&nbsp;f(int);&nbsp;//#1&nbsp;&nbsp;</span></li><li><span>void&nbsp;f(char&nbsp;*);//#2&nbsp;&nbsp;</span></li><li><span>//C++03&nbsp;&nbsp;</span></li><li>f(0);&nbsp;<span>//调用的是哪个&nbsp;f?&nbsp;&nbsp;</span></li><li><span>//C++11&nbsp;&nbsp;</span></li><li>f(nullptr)&nbsp;<span>//毫无疑问，调用的是&nbsp;#2&nbsp;&nbsp;</span></li></ol></div>  <br /> 所有跟指针有关的地方都可以用 nullptr，包括函数指针和成员指针：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>const&nbsp;char&nbsp;*pc=str.c_str();&nbsp;//data&nbsp;pointers&nbsp;&nbsp;</span></li><li><span>if&nbsp;(pc!=nullptr)&nbsp;&nbsp;</span></li><li>&nbsp;&nbsp;cout&lt;&lt;pc&lt;&lt;endl;&nbsp;&nbsp;</li><li><span>int&nbsp;(A::*pmf)()=nullptr;&nbsp;//指向成员函数的指针&nbsp;&nbsp;</span></li><li><span>void&nbsp;(*pmf)()=nullptr;&nbsp;//指向函数的指针&nbsp;&nbsp;</span></li></ol></div>  <h2>委托构造函数</h2> <p>C++11 中构造函数可以调用同一个类的另一个构造函数：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>class&nbsp;M&nbsp;//C++11&nbsp;delegating&nbsp;constructors&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;<span>int&nbsp;x,&nbsp;y;&nbsp;&nbsp;</span></li><li>&nbsp;<span>char&nbsp;*p;&nbsp;&nbsp;</span></li><li><span>public:&nbsp;&nbsp;</span></li><li>&nbsp;M(<span>int&nbsp;v)&nbsp;:&nbsp;x(v),&nbsp;y(0),&nbsp;&nbsp;p(new&nbsp;char&nbsp;[MAX])&nbsp;&nbsp;{}&nbsp;//#1&nbsp;target&nbsp;&nbsp;</span></li><li>&nbsp;M():&nbsp;M(0)&nbsp;{cout&lt;&lt;<span>"delegating&nbsp;ctor"&lt;&lt;end;}&nbsp;//#2&nbsp;delegating&nbsp;&nbsp;</span></li></ol></div>  <br /> #2 就是所谓的委托构造函数，调用了真正的构造函数 #1。 <h2>右值引用</h2> <p>在 C++03 中的引用类型是只绑定左值的，C++11 引用一个新的引用类型叫右值引用类型，它是绑定到右值的，如临时对象或字面量。<br /> 增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同，move  的意思是目标对象&#8220;窃取&#8221;原对象的资源，并将源置于&#8220;空&#8221;状态。当拷贝一个对象时，其实代价昂贵且无必要，move 操作就可以替代它。如在  string 交换的时候，使用 move 意义就有巨大的性能提升，如原方案是这样的：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>void&nbsp;naiveswap(string&nbsp;&amp;a,&nbsp;string&nbsp;&amp;&nbsp;b)&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>&nbsp;string&nbsp;temp&nbsp;=&nbsp;a;&nbsp;&nbsp;</li><li>&nbsp;a=b;&nbsp;&nbsp;</li><li>&nbsp;b=temp;&nbsp;&nbsp;</li><li>}&nbsp;&nbsp;</li></ol></div>  <br /> 这种方案很傻很天真，很慢，因为需要申请内存，然后拷贝字符，而 move 就只需要交换两个数据成员，无须申请、释放内存和拷贝字符数组：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>void&nbsp;moveswapstr(string&amp;&nbsp;empty,&nbsp;string&nbsp;&amp;&nbsp;filled)&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li><span>//pseudo&nbsp;code,&nbsp;but&nbsp;you&nbsp;get&nbsp;the&nbsp;idea&nbsp;&nbsp;</span></li><li>&nbsp;<span>size_t&nbsp;sz=empty.size();&nbsp;&nbsp;</span></li><li>&nbsp;<span>const&nbsp;char&nbsp;*p=&nbsp;empty.data();&nbsp;&nbsp;</span></li><li><span>//move&nbsp;filled's&nbsp;resources&nbsp;to&nbsp;empty&nbsp;&nbsp;</span></li><li>&nbsp;empty.setsize(filled.size());&nbsp;&nbsp;</li><li>&nbsp;empty.setdata(filled.data());&nbsp;&nbsp;</li><li><span>//filled&nbsp;becomes&nbsp;empty&nbsp;&nbsp;</span></li><li>&nbsp;filled.setsize(sz);&nbsp;&nbsp;</li><li>&nbsp;filled.setdata(p);&nbsp;&nbsp;</li><li>}&nbsp;&nbsp;</li></ol></div>  <br /> 要实现支持 move 的类，需要声明 move 构造函数和 move 赋值操作符，如下：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>class&nbsp;Movable&nbsp;&nbsp;</span></li><li>{&nbsp;&nbsp;</li><li>Movable&nbsp;(Movable&amp;&amp;);&nbsp;<span>//move&nbsp;constructor&nbsp;&nbsp;</span></li><li>Movable&amp;&amp;&nbsp;operator=(Movable&amp;&amp;);&nbsp;<span>//move&nbsp;assignment&nbsp;operator&nbsp;&nbsp;</span></li><li>};&nbsp;&nbsp;</li></ol></div>  <br /> C++11 的标准库广泛使用 move 语义，很多算法和容器都已经使用 move 语义优化过了。 <h2>C++11 的标准库</h2> <p>除 TR1 包含的新容器（unordered_set, unordered_map, unordered_multiset,  和unordered_multimap），还有一些新的库，如正则表达式，tuple，函数对象封装器等。下面介绍一些 C++11 的标准库新特性：</p> <h3>线程库</h3> <p>从程序员的角度来看，C++11 最重要的特性就是并发了。C++11 提供了 thread 类，也提供了 promise 和 future  用以并发环境中的同步，用 async() 函数模板执行并发任务，和 thread_local  存储声明为特定线程独占的数据，这里（http://www.devx.com/SpecialReports/Article/38883）有一个简单 的 C++11 线程库教程（英文）。</p> <h3>新的智能指针类</h3> <p>C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用，C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容，可以安全地把智能指针存入标准容器，也可以安全地用标准算法&#8220;倒腾&#8221;它们。</p> <h3>新的算法</h3> <p>主要是 all_of()、any_of() 和 none_of()，下面是例子：<br /> </p><div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>#include&nbsp;&lt;algorithm&gt;&nbsp;&nbsp;</span></li><li><span>//C++11&nbsp;code&nbsp;&nbsp;</span></li><li><span>//are&nbsp;all&nbsp;of&nbsp;the&nbsp;elements&nbsp;positive?&nbsp;&nbsp;</span></li><li>all_of(first,&nbsp;first+n,&nbsp;ispositive());&nbsp;<span>//false&nbsp;&nbsp;</span></li><li><span>//is&nbsp;there&nbsp;at&nbsp;least&nbsp;one&nbsp;positive&nbsp;element?&nbsp;&nbsp;</span></li><li>any_of(first,&nbsp;first+n,&nbsp;ispositive());<span>//true&nbsp;&nbsp;</span></li><li><span>//&nbsp;are&nbsp;none&nbsp;of&nbsp;the&nbsp;elements&nbsp;positive?&nbsp;&nbsp;</span></li><li>none_of(first,&nbsp;first+n,&nbsp;ispositive());&nbsp;<span>//false&nbsp;&nbsp;</span></li></ol></div>  <br /> 还有一个新的 copy_n：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>#include&nbsp;&lt;algorithm&gt;&nbsp;&nbsp;</span></li><li><span>int&nbsp;source[5]={0,12,34,50,80};&nbsp;&nbsp;</span></li><li><span>int&nbsp;target[5];&nbsp;&nbsp;</span></li><li><span>//从&nbsp;source&nbsp;拷贝&nbsp;5&nbsp;个元素到&nbsp;target&nbsp;&nbsp;</span></li><li>copy_n(source,5,target);&nbsp;&nbsp;</li></ol></div>  <br /> iota() 算法可以用来创建递增序列，它先把初值赋值给 *first，然后用前置 ++ 操作符增长初值并赋值到给下一个迭代器指向的元素，如下：<br /> <div bg_cpp=""><div><div><a href="http://blog.csdn.net/lanphaday/article/details/6564162#" title="view plain">view plain</a></div></div><ol start="1"><li><span>#include&nbsp;&lt;numeric&gt;&nbsp;&nbsp;</span></li><li><span>int&nbsp;a[5]={0};&nbsp;&nbsp;</span></li><li><span>char&nbsp;c[3]={0};&nbsp;&nbsp;</span></li><li>iota(a,&nbsp;a+5,&nbsp;10);&nbsp;<span>//changes&nbsp;a&nbsp;to&nbsp;{10,11,12,13,14}&nbsp;&nbsp;</span></li><li>iota(c,&nbsp;c+3,&nbsp;<span>'a');&nbsp;//{'a','b','c'}&nbsp;&nbsp;</span></li></ol></div>  <br /> 是的，C++11 仍然缺少一些很有用的库如 XML API，socket，GUI、反射&#8212;&#8212;以及自动垃圾收集。然而现有特性已经让 C++  更安全、高效（是的，效率更高了，可以参见 Google 的  基准测试结果http://www.itproportal.com/2011/06/07/googles-rates-c-most- complex-highest-performing-language/）以及更加易于学习和使用。<br /> 如果觉得 C++ 变化太大了，不必惊恐，花点时间来学习就好了。可能在你融会贯通新特性以后，你会同意 Stroustrup 的观点：C++11 是一门新的语言&#8212;&#8212;一个更好的 C++。<p><br /></p> </div></div></div><img src ="http://www.cppblog.com/zhaoyg/aggbug/153882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2011-08-19 21:06 <a href="http://www.cppblog.com/zhaoyg/archive/2011/08/19/153882.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】BOOST的编译选项</title><link>http://www.cppblog.com/zhaoyg/archive/2011/02/16/140148.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 16 Feb 2011 03:35:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2011/02/16/140148.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/140148.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2011/02/16/140148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/140148.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/140148.html</trackback:ping><description><![CDATA[<br>原文：<a href="http://www.cppblog.com/flyinghare/archive/2010/09/07/126078.aspx">http://www.cppblog.com/flyinghare/archive/2010/09/07/126078.aspx</a><br><br>许多新人对于编译BOOST感到无从下手，甚至因此而放弃使用BOOST，那真的太可惜了，下面我把一些常用的BOOST编译方法贴于此，同时也作为自己的笔记。 <br>首先下载bjam.exe，复制到 $BOOST$ 目录下。或者<span style="FONT-SIZE: 14pt"><span style="COLOR: #ff0000">自己生成bjam，打开Visual Studio 2008 命令提示窗口$BOOST$\tools\jam\src，执行 build.bat 会在$BOOST$\tools\jam\src\bin.ntx86 生成 bjam.exe 文件。复制文件 bjam.exe&nbsp; 文件到 $BOOST$\下</span></span><span style="COLOR: #ff0000"></span>。 <br><br>1.完全编译安装： <br>bjam --toolset=msvc install <br>完成后会生成一个bin.v2编译时的临时目录，手动删除。生成另一个目录C:\boost，里面为所有的头文件和库文件。头文件目录为boost_1_34_1\boost目录复制过去的。<br>&nbsp;<br>2.只编译相应的库文件 <br>bjam --toolset=msvc stage <br>完成后同样会生成bin.v2临时目录。另一个目录为stage文件，里面有对应的库文件。 <br><br>3.查看需要编译才能使用的库列表 <br>bjam --show-libraries <br><br>4.编译特定的库，如只编译regex <br>bjam --toolset=msvc --with-regex stage <br>生成的库文件在stage目录中。 <br><br>5.不编译某个库，如不编译regex <br>bjam --toolset=msvc --without-regex stage <br>生成的库文件在stage目录中。 <br><br>6.编译特定的库，如只编译regex，生成debug，多线程，共享连接版本，并保存在stage。 <br>bjam --toolset=msvc --with-regex stage debug threading=multi link=shared <br><br>7.生成 mt-sgd 的静态库(runtime-link-static) <br>bjam "-sTOOLS=vc-8_0" --with-thread install debug release runtime-link=static <br><br>8.编译regex库。 <br>bjam --toolset=msvc --with-regex stage debug release threading=multi threading=single link=shared link=static runtime-link=shared runtime-link=static <br><br>boost的安装方法： <br>对于DLL版本 <br>bjam --toolset=msvc link=shared runtime-link=shared threading=multi stage debug release install <br><br>对于lib版本 <br>bjam --toolset=msvc link=static runtime-link=shared threading=multi stage debug release install <br><br><br>另外，在$BOOST$\tools\build\v2\user-config.jam找到下面的地文 <br># ------------------- <br># MSVC configuration. <br># ------------------- <br># Configure msvc (default version, searched for in standard locations and PATH). <br># using msvc ; <br># Configure specific msvc version (searched for in standard locations and PATH). <br># using msvc : 8.0 ; <br>#在这里添加 vs2008 的配置 <br>using msvc : 9.0 : : /wd4819 /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE&nbsp; /D_SECURE_SCL=0 ; <br>#在这里添加 vs2005 的配置 <br>using msvc : 8.0 : : &lt;compileflags&gt;/wd4819 &lt;compileflags&gt;/D_CRT_SECURE_NO_DEPRECATE &lt;compileflags&gt;/D_SCL_SECURE_NO_DEPRECATE &lt;compileflags&gt;/D_SECURE_SCL=0 ;&nbsp;&nbsp;&nbsp; <br>然后进入 $BOOST$ 目录，执行bjam.exe 编译命令 <br>//下面的命令的各选项的说明： <br>//prefix&nbsp;&nbsp;&nbsp; 将boost安装到的路径（生成的头文件和库文件都会放到该路径中）。 <br>//重定义以下变量（利用-s设置）： <br>//VC80_ROOT　　 vc2005的安装路径，如果未将vc2005安装到默认位置，你必须指定该项。 <br>//TOOLS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用的编译工具，vc2005对应的是vc-8_0 <br>//PYTHON_ROOT&nbsp;&nbsp; ython的安装目录，如果未将BOOST安装到默认位置，你必须指定该项。 <br>//BUILD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译结果选项，默认会生成尽可能多的版本，如调试版／发行版，静态库／动态库，单线程／多线程。 <br>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/140148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2011-02-16 11:35 <a href="http://www.cppblog.com/zhaoyg/archive/2011/02/16/140148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】function/bind的救赎（上）</title><link>http://www.cppblog.com/zhaoyg/archive/2011/01/20/138983.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 20 Jan 2011 14:24:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2011/01/20/138983.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/138983.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2011/01/20/138983.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/138983.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/138983.html</trackback:ping><description><![CDATA[<p>转自：<a href="http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx">http://blog.csdn.net/myan/archive/2010/10/09/5928531.aspx</a><br>作者：孟岩<br>--------------------------------------------------------------------------------------<br>这是那篇C++0X的正文。太长，先写上半部分发了。</p>
<p>Function/bind可以是一个很简单的话题，因为它其实不过就是一个泛型的函数指针。但是如果那么来谈，就没意思了，也犯不上写这篇东西。在我看来，这个事情要讲的话，就应该讲透，讲到回调（callback）、代理（delegate）、信号（signal）和消息传递（messaging）的层面，因为它确实是太重要了。这个话题不但与面向对象的核心思想密切相关，而且是面向对象两大流派之间交锋的中心。围绕这个问题的思考和争论，几乎把20年来所有主流的编程平台和编程语言都搅进来了。所以，如果详尽铺陈，这个话题直接可以写一本书。</p>
<p>写书我当然没那个水平，但这个题目确实一直想动一动。然而这个主题实在太大，我实在没有精力把它完整的写下来；这个主题也很深，特别是涉及到并发环境有关的话题，我的理解还非常肤浅，总觉得我认识的很多高手都比我更有资格写这个话题。所以犹豫了很久，要不要现在写，该怎么写。最后我觉得，确实不能把一篇博客文章写成一本20年面向对象技术史记，所以决定保留大的架构，但是对其中具体的技术细节点到为止。我不会去详细地列举代码，分析对象的内存布局，画示意图，但是会把最重要的结论和观点写下来，说得好听一点是提纲挈领，说的不好听就是语焉不详。但无论如何，我想这样一篇东西，一是谈谈我对这个事情的看法，二是&#8220;抛砖引玉&#8221;，引来高手的关注，引出更深刻和完整的叙述。</p>
<p>下面开始。</p>
<p>0. 程序设计有一个范式（paradigm）问题。所谓范式，就是组织程序的基本思想，而这个基本思想，反映了程序设计者对程序的一个基本的哲学观，也就是说，他认为程序的本质是什么，他认为一个大的程序是由什么组成的。而这，又跟他对于现实世界的看法有关。显然，这样的看法不可能有很多种。编程作为一门行业，独立存在快60年了，但是所出现的范式不过三种——过程范式、函数范式、对象范式。其中函数范式与现实世界差距比较大，在这里不讨论。而过程范式和对象范式可以视为对程序本质的两种根本不同的看法，而且能够分别在现实世界中找到相应的映射。<br>过程范式认为，程序是由一个又一个过程经过顺序、选择和循环的结构组合而成。反映在现实世界，过程范式体现了劳动分工之前&#8220;全能人&#8221;的工作特点——所有的事情都能干，所有的资源都是我的，只不过得具体的事情得一步步地来做。 <br>对象范式则反映了劳动分工之后的团队协作的工作特点——每个人各有所长，各司其职，有各自的私有资源，工件和信息在人们之间彼此传递，最后完成工作。因此，对象范式也就形成了自己对程序的看法——程序是由一组对象组成，这些对象各有所能，通过消息传递实现协作。 </p>
<p>对象范式与过程范式相比，有三个突出的优势，第一，由于实现了逻辑上的分工，降低了大规模程序的开发难度。第二，灵活性更好——若干对象在一起，可以灵活组合，可以以不同的方式协作，完成不同的任务，也可以灵活的替换和升级。第三，对象范式更加适应图形化、网络化、消息驱动的现代计算环境。</p>
<p>所以，较之于过程范式，对象范式，或者说&#8220;面向对象&#8221;，确实是更具优势的编程范式。最近看到一些文章抨击面向对象，说面向对象是胡扯，我认为要具体分析。对面向对象的一部分批评，是冲着主流的&#8220;面向对象&#8221;语言去的，这确实是有道理的，我在下面也会谈到，而且会骂得更狠。而另一个批评的声音，主要而来自STL之父Alex Stepanov，他说的当然有他的道理，不过要知道该牛人是前苏联莫斯科国立罗蒙诺索夫大学数学系博士，你只要翻翻前苏联的大学数学教材就知道了，能够在莫大拿到数学博士的，根本就是披着人皮的外星高等智慧。而我们编写地球上的程序，可能还是应该以地球人的观点为主。</p>
<p>1. 重复一遍对象范式的两个基本观念：<br>程序是由对象组成的； <br>对象之间互相发送消息，协作完成任务； </p>
<p>请注意，这两个观念与后来我们熟知的面向对象三要素&#8220;封装、继承、多态&#8221;根本不在一个层面上，倒是与再后来的&#8220;组件、接口&#8221;神合。</p>
<p>2. 世界上第一个面向对象语言是Simula-67，第二个面向对象语言是Smalltalk-71。Smalltalk受到了Simula-67的启发，基本出发点相同，但也有重大的不同。先说相同之处，Simula和Smalltalk都秉承上述对象范式的两个基本观念，为了方便对象的构造，也都引入了类、继承等概念。也就是说，类、继承这些机制是为了实现对象范式原则而构造出来的第二位的、工具性的机制，那么为什么后来这些第二位的东西篡了主位，后面我会再来分析。而Simula和Smalltalk最重大的不同，就是Simula用方法调用的方式向对象发送消息，而Smalltalk构造了更灵活和更纯粹的消息发送机制。</p>
<p>具体的说，向一个Simula对象中发送消息，就是调用这个对象的一个方法，或者称成员函数。那么你怎么知道能够在这个对象上调用这个成员函数呢？或者说，你怎么知道能够向这个对象发送某个消息呢？这就要求你必须确保这个对象具有合适的类型，也就是说，你得先知道哦这个对象是什么，才能向它发消息。而消息的实现方式被直接处理为成员函数调用，或虚函数调用。</p>
<p>而Smalltalk在这一点上做了一个历史性的跨越，它实现了一个与目标对象无关的消息发送机制，不管那个对象是谁，也不管它是不是能正确的处理一个消息，作为发送消息的对象来说，可以毫无顾忌地抓住一个对象就发消息过去。接到消息的对象，要尝试理解这个消息，并最后调用自己的过程来处理消息。如果这个消息能被处理，那个对象自然会处理好，如果不能被处理，Smalltalk系统会向消息的发送者回传一个doesNotUnderstand消息，予以通知。对象不用关心消息是如何传递给另一个对象的，传递过程被分离出来（而不是像Simula那样明确地被以成员函数调用的方式实现），可以是在内存中复制，也可以是进程间通讯。到了Smalltalk-80时，消息传递甚至可以跨越网络。</p>
<p>为了方便后面的讨论，不妨把源自Simula的消息机制称为&#8220;静态消息机制&#8221;，把源自Smalltalk的消息机制称为&#8220;动态消息机制&#8221;。</p>
<p>Simula与Smalltalk之间对于消息机制的不同选择，主要是因为两者于用途。前者是用于仿真程序开发，而后者用于图形界面环境构建，看上去各自合情合理。然而，就是这么一点简单的区别，却造成了巨大的历史后果。</p>
<p>3. 到了1980年代，C++出现了。Bjarne Stroustrup在博士期间深入研究过Simula，非常欣赏其思想，于是就在C语言语法的基础之上，几乎把Simula的思想照搬过来，形成了最初的C++。C++问世以之初，主要用于解决规模稍大的传统类型的编程问题，迅速取得了巨大的成功，也证明了对象范式本身所具有的威力。</p>
<p>大约在同期，Brad Cox根据Smalltalk的思想设计了Objective-C，可是由于其语法怪异，没有流行起来。只有Steve Jobs这种具有禅宗美学鉴赏力的世外高人，把它奉为瑰宝，与1988年连锅把Objective-C的团队和产品一口气买了下来。</p>
<p>4. 就在同一时期，GUI成为热门。虽然GUI的本质是对象范型的，但是当时（1980年代中期）的面向对象语言，包括C++语言，还远不成熟，因此最初的GUI系统无一例外是使用C和汇编语言开发的。或者说，最初的GUI开发者硬是用抽象级别更低的语言构造了一个面向对象系统。熟悉Win32 SDK开发的人，应该知道我在说什么。</p>
<p>5. 当时很多人以为，如果C++更成熟些，直接用C++来构造Windows系统会大大地容易。也有人觉得，尽管Windows系统本身使用C写的，但是其面向对象的本质与C++更契合，所以在其基础上包装一个C++的GUI framework一定是轻而易举。可是一动手人们就发现，完全不是那么回事。用C++开发Windows框架难得要死。为什么呢？主要就是Windows系统中的消息机制实际上是动态的，与C++的静态消息机制根本配合不到一起去。在Windows里，你可以向任何一个窗口发送消息，这个窗口自己会在自己的wndproc里来处理这个消息，如果它处理不了，就交给default window/dialog proc去处理。而在C++里，你要向一个窗口发消息，就得确保这个窗口能处理这个消息，或者说，具有合适的类型。这样一来的话，就会导致一个错综复杂的窗口类层次结构，无法实现。而如果你要让所有的窗口类都能处理所有可能的消息，且不论这样在逻辑上就行不通（用户定义的消息怎么处理？），单在实现上就不可接受——为一个小小的不同就得创造一个新的窗口类，每一个小小的窗口类都要背上一个多达数百项的v-table，而其中可能99%的项都是浪费，不要说在当时，就是在今天，内存数量非常丰富的时候，如果每一个GUI程序都这么搞，用户也吃不消。</p>
<p>6. 实际上C++的静态消息机制还引起了更深严重的问题——扭曲了人们对面向对象的理解。既然必须要先知道对象的类型，才能向对象发消息，那么&#8220;类&#8221;这个概念就特别重要了，而对象只不过是类这个模子里造出来的东西，反而不重要。渐渐的，&#8220;面向对象编程&#8221;变成了&#8220;面向类编程&#8221;，&#8220;面向类编程&#8221;变成了&#8220;构造类继承树&#8221;。放在眼前的鲜活的对象活动不重要了，反而是其背后的静态类型系统成为关键。&#8220;封装、继承&#8221;这些第二等的特性，喧宾夺主，俨然成了面向对象的要素。每个程序员似乎都要先成为领域专家，然后成为领域分类学专家，然后构造一个完整的继承树，然后才能new出对象，让程序跑起来。正是因为这个过程太漫长，太困难，再加上C++本身的复杂度就很大，所以C++出现这么多年，真正堪称经典的面向对象类库和框架，几乎屈指可数。很多流行的库，比如MFC、iostream，都暴露出不少问题。一般程序员总觉得是自己的水平不够，于是下更大功夫去练剑。殊不知根本上是方向错了，脱离了对象范式的本质，企图用静态分类法来对现实世界建模，去刻画变化万千的动态世界。这么难的事，你水平再高也很难做好。</p>
<p>可以从一个具体的例子来理解这个道理，比如在一个GUI系统里，一个 Push Button 的设计问题。事实上在一个实际的程序里，一个 push button 到底&#8220;是不是&#8221;一个 button，进而是不是一个 window/widget，并不重要，本质上我根本不关心它是什么，它从属于哪一个类，在继承树里处于什么位置，只要那里有这么一个东西，我可以点它，点完了可以发生相应的效果，就可以了。可是Simula &#8211;&gt; C++ 所鼓励的面向对象设计风格，非要上来就想清楚，a Push Button is-a Button, a Button is-a Command-Target Control, a Command-Target Control is-a Control, a Control is-a Window. 把这一圈都想透彻之后，才能 new 一个 Push Button，然后才能让它工作。这就形而上学了，这就脱离实际了。所以很难做好。你看到 MFC 的类继承树，觉得设计者太牛了，能把这些层次概念都想清楚，自己的水平还不够，还得修炼。实际上呢，这个设计是经过数不清的失败和钱磨出来、砸出来的，MFC的前身 Afx 不是就失败了吗？1995年还有一个叫做 Taligent 的大项目，召集了包括 Eric Gamma 在内的一大堆牛人，要用C++做一个一统天下的application framework，最后也以惨败告终，连公司都倒闭了，CEO车祸身亡，牛人们悉数遣散。附带说一下，这个Taligent项目是为了跟NextSTEP和Microsoft Cairo竞争，前者用Objective-C编写，后来发展为Cocoa，后者用传统的Win32 + COM作为基础架构，后来发展为Windows NT。而Objective-C和COM，恰恰就在动态消息分派方面，与C++迥然不同。后面还会谈到。</p>
<p>客观地说，&#8220;面向类的设计&#8221;并不是没有意义。来源于实践又高于实践的抽象和概念，往往能更有力地把握住现实世界的本质，比如MVC架构，就是这样的有力的抽象。但是这种抽象，应该是来源于长期最佳实践的总结和提高，而不是面对问题时主要的解决思路。过于强调这种抽象，无异于假定程序员各个都是哲学家，具有对现实世界准确而深刻的抽象能力，当然是不符合实际情况的。结果呢，刚学习面向对象没几天的程序员，对眼前鲜活的对象世界视而不见，一个个都煞有介事地去搞哲学冥想，企图越过现实世界，去抽象出其背后本质，当然败得很惨。</p>
<p>其实C++问世之后不久，这个问题就暴露出来了。第一个C++编译器 Cfront 1.0 是单继承，而到了 Cfront 2.0，加入了多继承。为什么？就是因为使用中人们发现逻辑上似乎完美的静态单继承关系，碰到复杂灵活的现实世界，就破绽百出——蝙蝠是鸟也是兽，水上飞机能飞也能游，它们该如何归类呢？本来这应该促使大家反思继承这个机制本身，但是那个时候全世界陷入继承狂热，于是就开始给继承打补丁，加入多继承，进而加入虚继承，。到了虚继承，明眼人一看便知，这只是一个语法补丁，是为了逃避职责而制造的一块无用的遮羞布，它已经完全已经脱离实践了——有谁在事前能够判断是否应该对基类进行虚继承呢？</p>
<p>到了1990年代中期，问题已经十分明显。UML中有一个对象活动图，其描述的就是运行时对象之间相互传递消息的模型。1994年Robert C. Martin在《Object-Oriented C++ Design Using Booch Method》中，曾建议面向对象设计从对象活动图入手，而不是从类图入手。而1995年出版的经典作品《Design Patterns》中，建议优先考虑组合而不是继承，这也是尽人皆知的事情。这些迹象表明，在那个时候，面向对象社区里的思想领袖们，已经意识到&#8220;面向类的设计&#8221;并不好用。只可惜他们的革命精神还不够。</p>
<p>7. 你可能要问，Java 和.NET也是用继承关系组织类库，并进行设计的啊，怎么那么成功呢？这里有三点应该注意。第一，C++的难不仅仅在于其静态结构体系，还有很多源于语言设计上的包袱，比如对C的兼容，比如没有垃圾收集机制，比如对效率的强调，等等。一旦把这些包袱丢掉，设计的难度确实可以大大下降。第二，Java和.NET的核心类库是在C++十几年成功和失败的经验教训基础之上，结合COM体系优点设计实现的，自然要好上一大块。事实上，在Java和.NET核心类库的设计中很多地方，体现的是基于接口的设计，和真正的基于对象的设计。有了这两个主角站台，&#8220;面向类的设计&#8221;不能喧宾夺主，也能发挥一些好的作用。第三，如后文指出，Java和.NET中分别对C++最大的问题——缺少对象级别的delegate机制做出了自己的回应，这就大大弥补了原来的问题。</p>
<p>尽管如此，Java还是沾染上了&#8220;面向类设计&#8221;的癌症，基础类库里就有很多架床叠屋的设计，而J2EE/Java EE当中，这种形而上学的设计也很普遍，所以也引发了好几次轻量化的运动。这方面我并不是太懂，可能需要真正的Java高手出来现身说法。我对Java的看法以前就讲过——平台和语言核心非常好，但风气不好，崇尚华丽繁复的设计，装牛逼的人太多。</p>
<p>至于.NET，我听陈榕介绍过，在设计.NET的时候，微软内部对于是否允许继承爆发了非常激烈的争论。很多资深高人都强烈反对继承。至于最后引入继承，很大程度上是营销需要压倒了技术理性。尽管如此，由于有COM的基础，又实现了非常彻底的delegate，所以 .NET 的设计水平还是很高的。它的主要问题不在这，在于太急于求胜，更新速度太快，基础不牢。当然，根本问题还是微软没有能够在Web和Mobile领域里占到多大的优势，也就使得.NET没有用武之地。</p>
<p>8. COM。COM的要义是，软件是由COM Components组成，components之间彼此通过接口相互通讯。这是否让你回想起本文开篇所提出的对象范型的两个基本原则？有趣的是，在COM的术语里，&#8220;COM Component &#8221; 与&#8220;object &#8221;通假，这就使COM的心思昭然若揭了。Don Box在Essential COM里开篇就说，COM是更好的C++，事实上就是告诉大家，形而上学的&#8220;面向类设计&#8221;不好使，还是回到对象吧。</p>
<p>用COM开发的时候，一个组件&#8220;是什么&#8221;不重要，它具有什么接口，也就是说，能够对它发什么消息，才是重要的。你可以用IUnknown::QueryInterface问组件能对哪一组消息作出反应。向组件分派消息也不一定要被绑定在方法调用上，如果实现了 IDispatch，还可以实现&#8220;自动化&#8221;调用，也就是COM术语里的 Automation，而通过 列集（mashal），可以跨进程、跨网络向另一组件发送消息，通过 moniker，可以在分布式系统里定位和发现组件。如果你抱着&#8220;对象——消息&#8221;的观念去看COM的设计，就会意识到，整个COM体系就是用规范如何做对象，如何发消息的。或者更直白一点，COM就是用C/C++硬是模拟出一个Smalltalk。而且COM的概念世界里没有继承，就其纯洁性而言，比Smalltalk还Smalltalk。在对象泛型上，COM达到了一个高峰，领先于那个时代，甚至于比它的继任.NET还要纯洁。</p>
<p>COM的主要问题是它的学习难度和安全问题，而且，它过于追求纯洁性，完全放弃了&#8220;面向类设计&#8221; 的机制，显得有点过。</p>
<p>9. 好像有点扯远了，其实还是在说正事。上面说到由于C++的静态消息机制，导致了形而上学的&#8220;面向类的设计&#8221;，祸害无穷。但实际上，C++是有一个补救机会的，那就是实现对象级别的delegate机制。学过.NET的人，一听delegate这个词就知道是什么意思，但Java里没有对应机制。在C++的术语体系里，所谓对象级别delegate，就是一个对象回调机制。通过delegate，一个对象A可以把一个特定工作，比如处理用户的鼠标事件，委托给另一个对象B的一个方法来完成。A不必知道B的名字，也不用知道它的类型，甚至都不需要知道B的存在，只要求B对象具有一个签名正确的方法，就可以通过delegate把工作交给B的这个方法来执行。在C语言里，这个机制是通过函数指针实现的，所以很自然的，在C++里，我们希望通过指向成员函数的指针来解决类似问题。</p>
<p>然而就在这个问题上，C++让人扼腕痛惜。</p>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/138983.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2011-01-20 22:24 <a href="http://www.cppblog.com/zhaoyg/archive/2011/01/20/138983.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么时候自动合成的赋值操作符不能用</title><link>http://www.cppblog.com/zhaoyg/archive/2010/05/08/114852.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sat, 08 May 2010 08:21:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/05/08/114852.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/114852.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/05/08/114852.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/114852.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/114852.html</trackback:ping><description><![CDATA[前一段时间写了类似于这样的一段代码<br>
<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;A<br>{<br></span><span style="COLOR: #0000ff">private</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;ptr;<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;&nbsp;&nbsp;A():ptr(NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>};<br><br>A&nbsp;one;<br>A&nbsp;two;<br>two&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;one;</span></div>
编译时发现，编译器提示说默认赋值操作符不能用，而当我去掉ptr成员的const关键字后，便好了。当时很纳闷这是怎么搞的。<br>后来在好心人的帮助下，才意识到ptr是个const，所以默认的赋值操作符不能对ptr进行赋值。<br><br>唉，当时知道是这个原因后真的是羞涩难当，自己明知道const不能被赋值是常识问题，但当时却死活没能找出原因。<br>而更要人命的是我之前在<a href="http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html"><font color=#1a8bc8>{偶尔学习C++标准} 之 [对象的复制 - 复制构造与赋值操作符] </font></a>中就已经对赋值操作符做了一些笔记，但我竟然把一些内容给忘了（也可能是压根就没注意）。<br><br>现在重新就自动合成的赋值操作符何时不能使用再单独做一次笔记。<br><br>当类中拥有以下一种或几种情况时，隐式定义的赋值操作符是不能使用的：<br>1.非静态的const成员变量<br>2.非静态的引用类型成员变量<br>3.非静态的类类型的成员变量的赋值操作符不可访问<br>4.（如果有）基类中的赋值操作符不可访问<br><br>注，对于拥有以上情况中的一种或多种的类，标准中的用词是：ill-formed。我不知道为什么要用ill-formed（字面意思：不规范），但不管怎样，对于ill-formed，编译器的行为就是，无法编译通过。<br><br>后记：希望这次不要再忘记这些内容了。 
<img src ="http://www.cppblog.com/zhaoyg/aggbug/114852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-05-08 16:21 <a href="http://www.cppblog.com/zhaoyg/archive/2010/05/08/114852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>枚举的插曲</title><link>http://www.cppblog.com/zhaoyg/archive/2010/04/29/114004.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 29 Apr 2010 15:01:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/04/29/114004.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/114004.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/04/29/114004.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/114004.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/114004.html</trackback:ping><description><![CDATA[<p>今天，我无意中将鼠标放在一个在类中定义的枚举成员后，弹出了一个含有该成员原型的提示信息，看到这个提示后我顿时愣住了...<br>如图：<br><img border=0 src="http://www.cppblog.com/images/cppblog_com/zhaoyg/enum.png"></p>
当时看到__unnamed_0000_1直接愣住了，哪来的__unnamed_0000_1？<br>正当我想问别人的时候，突然想到了答案，原来答案很简单。因为TET是enum的一个成员，既然是成员，那么必然有一个作用域了，而我定义的是匿名的枚举，所以编译器就以__unnamed_0000_1来命名了。
<img src ="http://www.cppblog.com/zhaoyg/aggbug/114004.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-04-29 23:01 <a href="http://www.cppblog.com/zhaoyg/archive/2010/04/29/114004.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 再次纠结于友元声明是否向外围引入名字</title><link>http://www.cppblog.com/zhaoyg/archive/2010/03/07/109091.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 07 Mar 2010 02:49:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/03/07/109091.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/109091.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/03/07/109091.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/109091.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/109091.html</trackback:ping><description><![CDATA[C++标准： <br /><br /><strong>3.</strong> A function first declared in a friend declaration has<strong><span style="COLOR: #ff0000">external linkage</span></strong> (3.5). Otherwise, the function retains its previous linkage (7.1.1). <br /><br />按照<strong>3</strong>中说的，下面代码是应该通过的 <br /><div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> X<br />{<br />    friend </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> f(); <br />};<br /><br /></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> main()<br />{<br />    f(); </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">因为类X中的友元函数f的声明有外部链接，所以，f可见</span><span style="COLOR: #008000"><br /></span><span style="COLOR: #000000">}<br /></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> f() { </span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> ok to define friend function in the class body </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"> }<br /></span></div><br />编译测试： <br />VC2005/2010通过 ，而GCC 4.4.3报错：无法找到名字f <br /><br /><br /><strong>5.</strong> A function can be defined in a friend declaration of a class if and only if the class is a non-local class (9.8),the function name is unqualified, and the function has namespace scope. <br />[Example: <br />class M { <br />    friend void f() { } // <span style="COLOR: #ff0000"><strong>definition of global f</strong></span>, a friend of M, <br />                        // not the definition of a member function <br />}; <br />—end example] <br />Such a function is implicitly inline. A friend function defined in a class is in the (lexical) scope of the class in which it is defined. A friend function defined outside the class is not (3.4.1). <br /><br />按照<strong>5</strong>中说的，既然在类中定义友元函数f，是"definition of global f"，那么也就可以理解为下面的代码是可以通过编译的 <br /><div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000"> X<br />{<br />    friend </span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000"> f(){ </span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> ok to define friend function in the class body </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"> }<br />};<br /><br /></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> main()<br />{<br />    f(); <br />} <br /></span></div><br />然而VC2005/2010和GCC4.4.3都不能通过 <br /><br />TCPL中对友元有如下说明： <br />  “一个友元声明不会给外围的作用域引进一个名字”  <br />  “一个友元函数或者需要在某个外围作用域里显式声明，或者以它的类或由该类派生的类作为一个参数(13．6节)，否则就无法调用这个友元了。” <br /><br />C++ primer有如下： <br />  “友元声明将已命名的类或非成员函数引入到外围作用域中。此外，友元函数可以在类的内部定义，该函数的作用域扩展到包围该类定义的作用域。”  <br />  “用友元引入的类名和函数（定义或声明），可以像预先声明的一样使用” <br />C++ primer中的例子： <br />class X { <br />        friend class Y; <br />        friend void f() { /* ok to define friend function in the class body */ } <br />}; <br />class Z { <br />        Y *ymem; // ok: declaration for class Y introduced by friend in X <br />        void g() { return ::f(); } // ok: declaration of f introduced by X <br />}; <br /><br /><br />标准中说的和C++ PRIMER一样，而和TCPL不一样。 <br />至于编译器，GCC做的和TCPL说的一样，而VC和标准说的部分一样（对于在友元声明出定义函数处有出入） <br /><br />个人总结 <br />两个编译器行为不同，所以尽量提前声明友元所声明的名字 <br /><br />以上只是个人的看法，如果有不对的地方，<span style="COLOR: #ff0000"><strong>恳请</strong></span>各位大大指正<br /><br /><img src ="http://www.cppblog.com/zhaoyg/aggbug/109091.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-03-07 10:49 <a href="http://www.cppblog.com/zhaoyg/archive/2010/03/07/109091.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 对象安全自杀的条件</title><link>http://www.cppblog.com/zhaoyg/archive/2010/02/11/107718.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 11 Feb 2010 10:53:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/02/11/107718.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/107718.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/02/11/107718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/107718.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/107718.html</trackback:ping><description><![CDATA[<p>对象如果要安全的自杀，得保证以下条件：&nbsp;</p>
<ul>
    <li>this对象是必须用 new操作符分配(而不是用new[]，也不是用placement new，也不是局部对象，也不是global对象)。 </li>
    <li>delete this后，不能访问该对象任何的成员变量及虚函数(delete this回收的是数据，这包括对象的数据成员以及vtable，不包括函数代码)。&nbsp;</li>
    <li>delete this后，不能再访问this指针。换句话说，你不能去检查它、将它和其他指针比较、和 NULL比较、打印它、转换它，以及其它的任何事情。 </li>
</ul>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/107718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-02-11 18:53 <a href="http://www.cppblog.com/zhaoyg/archive/2010/02/11/107718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对右值类的一点小认识</title><link>http://www.cppblog.com/zhaoyg/archive/2010/02/10/107129.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 10 Feb 2010 13:35:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/02/10/107129.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/107129.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/02/10/107129.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/107129.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/107129.html</trackback:ping><description><![CDATA[<div style="BORDER-BOTTOM: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: #eeeeee; PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; WORD-BREAK: break-all; BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; PADDING-TOP: 4px"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;T{&nbsp;</span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"><img src="http://www.cppblog.com/Images/dot.gif"></span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000">&nbsp;};<br><br>T&nbsp;fun(){&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;T();&nbsp;&nbsp;}<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;fun()&nbsp;</span><span style="COLOR: #000000">= </span><span style="COLOR: #000000">T();<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br>}<br></span></div>
<p><br>当我看到上面这样的代码时，我便认为这个代码无法编译通过的，因为按我所知道的，函数fun所返回的是一个临时对象，而临时对象是不能被修改的，然而fun()=T()语句便恰恰是在对临时对象进行修改。但是当我编译之后却傻眼了，居然通过了，我很纳闷。<br>后来在网友的帮助下，在标准中找到了答案：<br><br><strong><em>3.10.5&nbsp; <br>The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.</em></strong></p>
<p><strong><em>13.5.7<br>The identities among certain predefined operators applied to basic types (for example, ++a &#8801; a+=1) need not hold for operator functions. Some predefined operators, such as +=, require an operand to be an lvalue when applied to basic types; this is not required by operator functions.</em></strong></p>
<p><strong><em>3.10<br>An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [Example: a member function called for an object (9.3) can modify the object. ]</em></strong><br><br><br>也就是说，在对内置类型进行赋值操作时，将调用内置的赋值操作符，而这种内置的要求左操作数必须是左值；而当对类类型对象进行赋值时，所调用的是重载的赋值操作符，但重载的操作符并没有要求必须使用左值，也就是说，赋值操作符的左操作数可以是右值。<br>后来得知，在C++中右值可以是一个对象，而&#8220;对象&#8221;就指的是&#8220;一段内存存贮区域&#8221;，但C中的右值则不是一个对象，他只是一个值。<br><br>以上内容如有不对之处，还望不惜指正。<br><br>对lvalue和rvalue的较详细介绍请看文章&lt;<font color=#800080>Lvalues and Rvalues</font>&gt;：<a href="http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html">http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html</a></p>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/107129.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-02-10 21:35 <a href="http://www.cppblog.com/zhaoyg/archive/2010/02/10/107129.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] Lvalues and Rvalues</title><link>http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sat, 06 Feb 2010 14:41:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/107405.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/107405.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/107405.html</trackback:ping><description><![CDATA[<p>C and C++ enforce subtle differences on the expressions to the left and right of the assignment operator <br><br>If you've been programming in either C or C++ for a while, it's likely that you've heard the terms lvalue (pronounced "ELL-value") and rvalue (pronounced "AR-value"), if only because they occasionally appear in compiler error messages. There's also a good chance that you have only a vague understanding of what they are. If so, it's not your fault. </p>
<p>Most books on C or C++ do not explain lvalues and rvalues very well. (I looked in a dozen books and couldn't find one explanation I liked.) This may be due to of the lack of a consistent definition even among the language standards. The 1999 C Standard defines lvalue differently from the 1989 C Standard, and each of those definitions is different from the one in the C++ Standard. And none of the standards is clear. </p>
<p>Given the disparity in the definitions for lvalue and rvalue among the language standards, I'm not prepared to offer precise definitions. However, I can explain the underlying concepts common to the standards. </p>
<p>As is often the case with discussions of esoteric language concepts, it's reasonable for you to ask why you should care. Admittedly, if you program only in C, you can get by without understanding what lvalues and rvalues really are. Many programmers do. But understanding lvalues and rvalues provides valuable insights into the behavior of built-in operators and the code compilers generate to execute those operators. If you program in C++, understanding the built-in operators is essential background for writing well-behaved overloaded operators. <br></p>
<p><span style="COLOR: red"><br>Basic concepts</span><br>--------------------------------------------------------------------------------</p>
<p>Kernighan and Ritchie coined the term lvalue to distinguish certain expressions from others. In The C Programming Language (Prentice-Hall, 1988), they wrote "An object is a manipulatable region of storage; an lvalue is an expression referring to an object....The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression." </p>
<p>In other words, the left and right operands of an assignment expression are themselves expressions. For the assignment to be valid, the left operand must refer to an object-it must be an lvalue. The right operand can be any expression. It need not be an lvalue. For example: </p>
<p>int n; </p>
<p>declares n as an object of type int. When you use n in an assignment expression such as: </p>
<p>n = 3; </p>
<p>n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue. </p>
<p>Suppose you switch the left and right operands around: </p>
<p>3 = n; </p>
<p>Unless you're a former Fortran programmer, this is obviously a silly thing to do. The assignment is trying to change the value of an integer constant. Fortunately, C and C++ compilers reject it as an error. The basis for the rejection is that, although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. It doesn't refer to an object; it just represents a value. </p>
<p>I don't know where the term rvalue comes from. Neither edition of the C Standard uses it, other than in a footnote stating "What is sometimes called 'rvalue' is in this standard described as the 'value of an expression.'" </p>
<p>The C++ Standard does use the term rvalue, defining it indirectly with this sentence: "Every expression is either an lvalue or an rvalue." So an rvalue is any expression that is not an lvalue. </p>
<p>Numeric literals, such as 3 and 3.14159, are rvalues. So are character literals, such as 'a'. An identifier that refers to an object is an lvalue, but an identifier that names an enumeration constant is an rvalue. For example: </p>
<p>enum color { red, green, blue };<br>color c;<br>...<br>c = green;&nbsp;&nbsp;&nbsp; // ok<br>blue = green;&nbsp;&nbsp;&nbsp; // error </p>
<p>The second assignment is an error because blue is an rvalue. </p>
<p>Although you can't use an rvalue as an lvalue, you can use an lvalue as an rvalue. For example, given: </p>
<p>int m, n; </p>
<p>you can assign the value in n to the object designated by m using: </p>
<p>m = n; </p>
<p>This assignment uses the lvalue expression n as an rvalue. Strictly speaking, a compiler performs what the C++ Standard calls an lvalue-to-rvalue conversion to obtain the value stored in the object to which n refers. <br></p>
<p><br><span style="COLOR: red">Lvalues in other expressions</span><br>-------------------------------------------------------------------------------</p>
<p>Although lvalues and rvalues got their names from their roles in assignment expressions, the concepts apply in all expressions, even those involving other built-in operators. </p>
<p>For example, both operands of the built-in binary operator + must be expressions. Obviously, those expressions must have suitable types. After conversions, both expressions must have the same arithmetic type, or one expression must have a pointer type and the other must have an integer type. But either operand can be either an lvalue or an rvalue. Thus, both x + 2 and 2 + x are valid expressions. </p>
<p>Although the operands of a binary + operator may be lvalues, the result is always an rvalue. For example, given integer objects m and n: </p>
<p>m + 1 = n; </p>
<p>is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to: </p>
<p>(m + 1) = n;&nbsp;&nbsp;&nbsp; // error </p>
<p>which is an error because m + 1 is an rvalue. </p>
<p>As another example, the unary &amp; (address-of) operator requires an lvalue as its operand. That is, &amp;n is a valid expression only if n is an lvalue. Thus, an expression such as &amp;3 is an error. Again, 3 does not refer to an object, so it's not addressable. </p>
<p>Although the unary &amp; requires an lvalue as its operand, it's result is an rvalue. For example: </p>
<p>int n, *p;<br>...<br>p = &amp;n;&nbsp;&nbsp;&nbsp; // ok<br>&amp;n = p;&nbsp;&nbsp;&nbsp; // error: &amp;n is an rvalue </p>
<p>In contrast to unary &amp;, unary * produces an lvalue as its result. A non-null pointer p always points to an object, so *p is an lvalue. For example: </p>
<p>int a[N];<br>int *p = a;<br>...<br>*p = 3;&nbsp;&nbsp;&nbsp;&nbsp; // ok </p>
<p>Although the result is an lvalue, the operand can be an rvalue, as in: </p>
<p>*(p + 1) = 4;&nbsp;&nbsp;&nbsp; // ok<br></p>
<p><br><span style="COLOR: red">Data storage for rvalues<br></span>--------------------------------------------------------------------------------</p>
<p>Conceptually, an rvalue is just a value; it doesn't refer to an object. In practice, it's not that an rvalue can't refer to an object. It's just that an rvalue doesn't necessarily refer to an object. Therefore, both C and C++ insist that you program as if rvalues don't refer to objects. </p>
<p>The assumption that rvalues do not refer to objects gives C and C++ compilers considerable freedom in generating code for rvalue expressions. Consider an assignment such as: </p>
<p>n = 1; </p>
<p>where n is an int. A compiler might generate named data storage initialized with the value 1, as if 1 were an lvalue. It would then generate code to copy from that initialized storage to the storage allocated for n. In assembly language, this might look like: </p>
<p>one: .word 1<br>...<br>mov (one), n </p>
<p>Many machines provide instructions with immediate operand addressing, in which the source operand can be part of the instruction rather than separate data. In assembly, this might look like: </p>
<p>mov #1, n </p>
<p>In this case, the rvalue 1 never appears as an object in the data space. Rather, it appears as part of an instruction in the code space. </p>
<p>On some machines, the fastest way to put the value 1 into an object is to clear it and then increment it, as in: </p>
<p>clr n<br>inc n </p>
<p>Clearing the object sets it to zero. Incrementing adds one. Yet data representing the values 0 and 1 appear nowhere in the object code. <br></p>
<p><br><span style="COLOR: red">More to come</span><br>--------------------------------------------------------------------------------</p>
<p>Although it's true that rvalues in C do not refer to objects, it's not so in C++. In C++, rvalues of a class type do refer to objects, but they still aren't lvalues. Thus, everything I've said thus far about rvalues is true as long as we're not dealing with rvalues of a class type. </p>
<p>Although lvalues do designate objects, not all lvalues can appear as the left operand of an assignment. I'll pick up with this in my next column.</p>
<p>如下：</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------<br><strong>Non-modifiable Lvalues</strong></p>
<p>--------------------------------------------------------------------------------</p>
<p><br>Lvalues actually come in a variety of flavors. If you really want to understand how compilers evaluate expressions, you'd better develop a taste. </p>
<p>const 限定符的含义： 比如 int const m;<br>它并不是说m的值不能被修改, 而是指 m 不能修改它引用的对象！<br>e.g: <br>int m;<br>int const *p = &amp;m;<br>m += 1;&nbsp; //right<br>*p += 1; //wrong<br>&nbsp;</p>
<p>An expression is a sequence of operators and operands that specifies a computation. That computation might produce a resulting value and it might generate side effects. An assignment expression has the form: </p>
<p>e1 = e2 </p>
<p>where e1 and e2 are themselves expressions. The right operand e2 can be any expression, but the left operand e1 must be an lvalue expression. That is, it must be an expression that refers to an object. As I explained last month ("Lvalues and Rvalues," June 2001, p. 70), the "l" in lvalue stands for "left," as in "the left side of an assignment expression." For example: </p>
<p>int n; </p>
<p>declares n as an object of type int. When you use n in an assignment expression such as: </p>
<p>n = 3; </p>
<p>the n is an expression (a subexpression of the assignment expression) referring to an int object. The expression n is an lvalue. On the other hand: </p>
<p>3 = n; </p>
<p>causes a compilation error, and well it should, because it's trying to change the value of an integer constant. Although the assignment's left operand 3 is an expression, it's not an lvalue. It's an rvalue. An rvalue is simply any expression that is not an lvalue. It doesn't refer to an object; it just represents a value. </p>
<p>Although lvalue gets its name from the kind of expression that must appear to the left of an assignment operator, that's not really how Kernighan and Ritchie defined it. In the first edition of The C Programming Language (Prentice-Hall, 1978), they defined an lvalue as "an expression referring to an object." At that time, the set of expressions referring to objects was exactly the same as the set of expressions eligible to appear to the left of an assignment operator. But that was before the const qualifier became part of C and C++. </p>
<p>The const qualifier renders the basic notion of lvalues inadequate to describe the semantics of expressions. We need to be able to distinguish between different kinds of lvalues. And that's what I'm about to show you how to do. But first, let me recap. </p>
<p><br><span style="COLOR: red">A few key points</span><br>--------------------------------------------------------------------------------</p>
<p>The assignment operator is not the only operator that requires an lvalue as an operand. The unary &amp; (address-of) operator requires an lvalue as its sole operand. That is, &amp;n is a valid expression only if n is an lvalue. Thus, an expression such as &amp;3 is an error. The literal 3 does not refer to an object, so it's not addressable. </p>
<p>Not only is every operand either an lvalue or an rvalue, but every operator yields either an lvalue or an rvalue as its result. For example, the binary + operator yields an rvalue. Given integer objects m and n: </p>
<p>m + 1 = n; </p>
<p>is an error. The + operator has higher precedence than the = operator. Thus, the assignment expression is equivalent to: </p>
<p>(m + 1) = n; // error </p>
<p>which is an error because m + 1 is an rvalue. </p>
<p>An operator may require an lvalue operand, yet yield an rvalue result. The unary &amp; is one such operator. For example: </p>
<p>int n, *p;<br>...<br>p = &amp;n; // ok<br>&amp;n = p; // error: &amp;n is an rvalue</p>
<p><br>On the other hand, an operator may accept an rvalue operand, yet yield an lvalue result, as is the case with the unary * operator. A valid, non-null pointer p always points to an object, so *p is an lvalue. For example: </p>
<p>int a[N];<br>int *p = a;<br>...<br>*p = 3; // ok </p>
<p>Although the result is an lvalue, the operand can be an rvalue, as in: </p>
<p>*(p + 1) = 4; // ok </p>
<p>With this in mind, let's look at how the const qualifier complicates the notion of lvalues. </p>
<p><br><span style="COLOR: red">Lvalues and the const qualifier<br></span>--------------------------------------------------------------------------------</p>
<p>A const qualifier appearing in a declaration modifies the type in that declaration, or some portion thereof. For example: int const n = 127; </p>
<p>declares n as object of type "const int." The expression n refers to an object, almost as if const weren't there, except that n refers to an object the program can't modify. For example, an assignment such as: </p>
<p>n = 0; // error, can't modify n </p>
<p>produces a compile-time error, as does: </p>
<p>++n; // error, can't modify n </p>
<p>(I covered the const qualifier in depth in several of my earlier columns. See "Placing const in Declarations," June 1998, p. 19 or "const T vs. T const," February 1999, p. 13, among others.) How is an expression referring to a const object such as n any different from an rvalue? After all, if you rewrite each of the previous two expressions with an integer literal in place of n, as in: </p>
<p>7 = 0; // error, can't modify literal ++7; // error, can't modify literal </p>
<p>they're both still errors. You can't modify n any more than you can an rvalue, so why not just say n is an rvalue, too? The difference is that you can take the address of a const object, but you can't take the address of an integer literal. For example: </p>
<p>int const *p;<br>...<br>p = &amp;n; // ok<br>p = &amp;7; // error </p>
<p>Notice that p declared just above must be a "pointer to const int." If you omitted const from the pointer type, as in: </p>
<p>int *p; </p>
<p>then the assignment: </p>
<p>p = &amp;n; // error, invalid conversion </p>
<p>would be an error. When you take the address of a const int object, you get a value of type "pointer to const int," which you cannot convert to "pointer to int" unless you use a cast, as in: </p>
<p>p = (int *)&amp;n; // (barely) ok </p>
<p>Although the cast makes the compiler stop complaining about the conversion, it's still a hazardous thing to do. (See "What const Really Means," August 1998, p. 11.) </p>
<p>Thus, an expression that refers to a const object is indeed an lvalue, not an rvalue. However, it's a special kind of lvalue called a non-modifiable lvalue-an lvalue that you can't use to modify the object to which it refers. This is in contrast to a modifiable lvalue, which you can use to modify the object to which it refers. </p>
<p>Once you factor in the const qualifier, it's no longer accurate to say that the left operand of an assignment must be an lvalue. Rather, it must be a non-modifiable lvalue. In fact, every arithmetic assignment operator, such as += and *=, requires a modifiable lvalue as its left operand. For all scalar types: </p>
<p>x += y; // arithmetic assignment </p>
<p>is equivalent to: </p>
<p>x = x + y; // assignment </p>
<p>except that it evaluates x only once. Since the x in this assignment must be a modifiable lvalue, it must also be a modifiable lvalue in the arithmetic assignment. Not every operator that requires an lvalue operand requires a modifiable lvalue. The unary &amp; operator accepts either a modifiable or a non-modifiable lvalue as its operand. For example, given: </p>
<p>int m;<br>int const n = 10; </p>
<p>&amp;m is a valid expression returning a result of type "pointer to int," and &amp;n is a valid expression returning a result of type "pointer to const int." </p>
<p><br><span style="COLOR: red">What it is that's really non-modifiable</span><br>--------------------------------------------------------------------------------<br>Earlier, I said a non-modifiable lvalue is an lvalue that you can't use to modify an object. Notice that I did not say a non-modifiable lvalue refers to an object that you can't modify-I said you can't use the lvalue to modify the object. The distinction is subtle but nonetheless important, as shown in the following example. Consider: </p>
<p>int n = 0;<br>int const *p;<br>...<br>p = &amp;n; </p>
<p>At this point, p points to n, so *p and n are two different expressions referring to the same object. However, *p and n have different types. As I explained in an earlier column ("What const Really Means"), this assignment uses a qualification conversion to convert a value of type "pointer to int" into a value of type "pointer to const int." Expression n has type "(non-const) int." It is a modifiable lvalue. Thus, you can use n to modify the object it designates, as in: </p>
<p>n += 2; </p>
<p>On the other hand, p has type "pointer to const int," so *p has type "const int." Expression *p is a non-modifiable lvalue. You cannot use *p to modify the object n, as in: </p>
<p>*p += 2; </p>
<p>even though you can use expression n to do it. Such are the semantics of const in C and C++. </p>
<p><br><span style="COLOR: red">In summary</span><br>--------------------------------------------------------------------------------<br>Every expression in C and C++ is either an lvalue or an rvalue. An lvalue is an expression that designates (refers to) an object. Every lvalue is, in turn, either modifiable or non-modifiable. An rvalue is any expression that isn't an lvalue. Operationally, the difference among these kinds of expressions is this: </p>
<ul>
    <li>A modifiable lvalue is addressable (can be the operand of unary &amp;) and assignable (can be the left operand of =).
    <li>A non-modifiable lvalue is addressable, but not assignable.
    <li>An rvalue is neither addressable nor assignable.&nbsp;
    <li>Again, as I cautioned last month, all this applies only to rvalues of a non-class type. Classes in C++ mess up these concepts even further.&nbsp; </li>
</ul>
<p>Dan Saks is a high school track coach and the president of Saks &amp; Associates, a C/C++ training and consulting company. You can write to him at <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#100;&#115;&#97;&#107;&#115;&#64;&#119;&#105;&#116;&#116;&#101;&#110;&#98;&#101;&#114;&#103;&#46;&#101;&#100;&#117;">dsaks@wittenberg.edu</a>. </p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/SeeSeaBee/archive/2007/09/08/1777120.aspx">http://blog.csdn.net/SeeSeaBee/archive/2007/09/08/1777120.aspx</a></p>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/107405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2010-02-06 22:41 <a href="http://www.cppblog.com/zhaoyg/archive/2010/02/06/107405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转帖]C++模板元编程</title><link>http://www.cppblog.com/zhaoyg/archive/2009/11/15/100972.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 15 Nov 2009 06:25:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/11/15/100972.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/100972.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/11/15/100972.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/100972.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/100972.html</trackback:ping><description><![CDATA[作者：荣耀<br>原文出处：<a href="http://www.royaloo.com/articles/articles_2003/Metaprogramming.htm" target=_blank><u><font color=#800080>http://www.royaloo.com/articles/articles_2003/Metaprogramming.htm</font></u></a><br><br><br><!-- DETAILS --><strong>摘要</strong><br><br>本文描述了模板元编程技术的起源、概念和机制，并介绍了模板元编程技术在Blitz++和Loki程序库中的应用。&nbsp;<br><br><strong>关键字</strong><br><br>编译期计算&nbsp; 模板元编程&nbsp; Blitz++&nbsp; Loki&nbsp;<br><br><strong>导言&nbsp;</strong><br><br>1994年，C++标准委员会在圣迭哥举行的一次会议期间Erwin Unruh展示了一段可以产生质数的代码。这段代码的特别之处在于质数产生于编译期而非运行期，在编译器产生的一系列错误信息中间夹杂着从2到某个设定值之间的所有质数：<br>&nbsp;<br><font color=#008000>// Prime number computation by Erwin Unruh</font>&nbsp;<br>template &lt;int i&gt; struct D { D(void*); operator int(); };&nbsp;<br><br>template &lt;int p, int i&gt; struct is_prime {&nbsp;<br>&nbsp;&nbsp;&nbsp; enum { prim = (p%i) &amp;&amp; is_prime&lt;(i &gt; 2 ? p : 0), i -1&gt; :: prim };&nbsp;<br>};&nbsp;<br><br>template &lt; int i &gt; struct Prime_print {&nbsp;<br>&nbsp;&nbsp;&nbsp; Prime_print&lt;i-1&gt; a;&nbsp;<br>&nbsp;&nbsp;&nbsp; enum { prim = is_prime&lt;i, i-1&gt;::prim };&nbsp;<br>&nbsp;&nbsp;&nbsp; void f() { D&lt;i&gt; d = prim; }&nbsp;<br>};&nbsp;<br><br>struct is_prime&lt;0,0&gt; { enum {prim=1}; };&nbsp;<br>struct is_prime&lt;0,1&gt; { enum {prim=1}; };&nbsp;<br>struct Prime_print&lt;2&gt; { enum {prim = 1}; void f() { D&lt;2&gt; d = prim; } };&nbsp;<br>#ifndef LAST&nbsp;<br>#define LAST 10&nbsp;<br>#endif&nbsp;<br>main () {&nbsp;<br>&nbsp;&nbsp;&nbsp; Prime_print&lt;LAST&gt; a;&nbsp;<br>}&nbsp;<br><br>类模板D只有一个参数为void*的构造器，而只有0才能被合法转换为void*。1994年，Erwin Unruh采用Metaware 编译器编译出错信息如下（以及其它一些信息，简短起见，它们被删除了）：&nbsp;<br>| Type `enum{}&#8242; can&#8242;t be converted to txpe `D&lt;2&gt;&#8242; ("primes.cpp",L2/C25).&nbsp;<br>| Type `enum{}&#8242; can&#8242;t be converted to txpe `D&lt;3&gt;&#8242; ("primes.cpp",L2/C25).&nbsp;<br>| Type `enum{}&#8242; can&#8242;t be converted to txpe `D&lt;5&gt;&#8242; ("primes.cpp",L2/C25).&nbsp;<br>| Type `enum{}&#8242; can&#8242;t be converted to txpe `D&lt;7&gt;&#8242; ("primes.cpp",L2/C25).&nbsp;<br>如今，上面的代码已经不再是合法的C++程序了。以下是Erwin Unruh亲手给出的修订版，可以在今天符合标准的C++编译器上进行编译：<br>&nbsp;<br><font color=#008000>// Prime number computation by Erwin Unruh</font>&nbsp;<br><br>template &lt;int i&gt; struct D { D(void*); operator int(); };&nbsp;<br><br>template &lt;int p, int i&gt; struct is_prime {&nbsp;<br>&nbsp;&nbsp;&nbsp; enum { prim = (p==2) || (p%i) &amp;&amp; is_prime&lt;(i&gt;2?p:0), i-1&gt; :: prim };&nbsp;<br>};&nbsp;<br><br>template &lt;int i&gt; struct Prime_print {&nbsp;<br>Prime_print&lt;i-1&gt; a;&nbsp;<br>&nbsp;&nbsp;&nbsp; enum { prim = is_prime&lt;i, i-1&gt;::prim };&nbsp;<br>&nbsp;&nbsp;&nbsp; void f() { D&lt;i&gt; d = prim ? 1 : 0; a.f();}&nbsp;<br>};&nbsp;<br><br>template&lt;&gt; struct is_prime&lt;0,0&gt; { enum {prim=1}; };&nbsp;<br>template&lt;&gt; struct is_prime&lt;0,1&gt; { enum {prim=1}; };&nbsp;<br><br>template&lt;&gt; struct Prime_print&lt;1&gt; {&nbsp;<br>&nbsp;&nbsp;&nbsp; enum {prim=0};&nbsp;<br>&nbsp;&nbsp;&nbsp; void f() { D&lt;1&gt; d = prim ? 1 : 0; };&nbsp;<br>};&nbsp;<br><br>#ifndef LAST&nbsp;<br>#define LAST 18&nbsp;<br>#endif&nbsp;<br><br>main() {&nbsp;<br>&nbsp;&nbsp;&nbsp; Prime_print&lt;LAST&gt; a;&nbsp;<br>&nbsp;&nbsp;&nbsp; a.f();&nbsp;<br>}&nbsp;<br>在GNU C++ (MinGW Special) 3.2中编译这段程序时，编译器将会给出如下出错信息（以及其它一些信息，简短起见，它们被删除了）：&nbsp;<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 17]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 13]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 11]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 7]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 5]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 3]'<br>Unruh.cpp:12: initializing argument 1 of `D&lt;i&gt;::D(void*) [with int i = 2]'&nbsp;<br>这个例子展示了可以利用模板实例化机制于编译期执行一些计算。这种通过模板实例化而执行的编译期计算技术即被称为模板元编程。&nbsp;<br><br><strong>一个可以运行的模板元编程例子</strong>&nbsp;<br><br>模板元编程（Template Metaprogramming）更准确的含义应该是&#8220;编&#8216;可以编程序的&#8217;程序&#8221;，而模板元程序（Template Metaprogram）则是&#8220;&#8216;可以编程序的&#8217;程序&#8221;。也就是说，我们给出代码的产生规则，编译器在编译期解释这些规则并生成新代码来实现我们预期的功能。&nbsp;<br>Erwin Unruh的那段经典代码并没有执行，它只是以编译出错信息的方式输出中间计算结果。让我们来看一个可以运行的模板元编程例子 — 计算给定整数的指定次方：&nbsp;<br><br><font color=#008000>// xy.h</font><br><br><font color=#008000>//原始摸板</font><br>template&lt;int Base, int Exponent&gt;<br>class XY<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; enum { result_ = Base * XY&lt;Base, Exponent-1&gt;::result_ };<br>};<br><br><font color=#008000>//用于终结递归的局部特化版</font><br>template&lt;int Base&gt;<br>class XY&lt;Base, 0&gt;&nbsp;<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; enum { result_ = 1 };<br>};<br>模板元编程技术之根本在于递归模板实例化。第一个模板实现了一般情况下的递归规则。当用一对整数&lt;X, Y&gt;来实例化模板时，模板XY&lt;X, Y&gt;需要计算其result_的值，将同一模板中针对&lt;X, Y-1&gt;实例化所得结果乘以X即可。第二个模板是一个局部特化版本，用于终结递归。&nbsp;<br>让我们看看使用此模板来计算5^4 （通过实例化XY&lt;5, 4&gt;）时发生了什么：&nbsp;<br><font color=#008000>// xytest.cpp</font><br><br>#include &lt;iostream&gt;<br>#include "xy.h"<br><br>int main()&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "X^Y&lt;5, 4&gt;::result_ = " &lt;&lt; XY&lt;5, 4&gt;::result_;<br>}<br>首先，编译器实例化XY&lt;5, 4&gt;，它的result_为5 * XY&lt;5, 3&gt;::result_，如此一来，又需要针对&lt;5, 3&gt;实例化同样的模板，后者又实例化XY&lt;5, 2&gt;&#8230;&#8230; 当实例化到XY&lt;5, 0&gt;的时候，result_的值被计算为1，至此递归结束。&nbsp;<br><strong><br>递归模板实例化的深度和终结条件</strong>&nbsp;<br><br>可以想象，如果我们以非常大的Y值来实例化类模板XY，那肯定会占用大量的编译器资源甚至会迅速耗尽可用资源（在计算结果溢出之前），因此，在实践中我们应该有节制地使用模板元编程技术。&nbsp;<br>虽然 C++标准建议的最小实例化深度只有17层，然而大多数编译器都能够处理至少几十层，有些编译器允许实例化至数百层，更有一些可达数千层，直至资源耗尽。&nbsp;<br>假如我们拿掉XY模板局部特化版本，情况会如何？&nbsp;<br><font color=#008000>// xy2.h<br><br>//原始摸板</font><br>template&lt;int Base, int Exponent&gt;<br>class XY<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; enum { result_ = Base * XY&lt;Base, Exponent-1&gt;::result_ };<br>};<br>测试程序不变：&nbsp;<br><font color=#008000>// xytest2.cpp</font><br><br>#include &lt;iostream&gt;<br>#include "xy2.h"<br><br>int main()&nbsp;<br>{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "X^Y&lt;5, 4&gt;::result_ = " &lt;&lt; XY&lt;5, 4&gt;::result_;<br>}<br>执行如下编译命令：&nbsp;<br>C:\&gt;g++ -c xytest2.cpp&nbsp;<br>你将会看到递归实例化将一直进行下去，直到达到编译器的极限。&nbsp;<br>GNU C++ (MinGW Special) 3.2的默认实例化极限深度为500层，你也可以手工调整实例化深度：&nbsp;<br>C:\&gt;g++ -ftemplate-depth-3400 -c xytest2.cpp&nbsp;<br>事实上，g++ 3.2允许的模板实例化极限深度还可以再大一些（我的测试结果是不超过3450层）。&nbsp;<br>因此，在使用模板元编程技术时，我们总是要给出原始模板的特化版（局部特化版或完全特化版或兼而有之），以作为递归模板实例化的终结准则。&nbsp;<br><br><strong>利用模板元编程技术解开循环</strong>&nbsp;<br><br>模板元编程技术最早的实际应用之一是用于数值计算中的解循环。举个例子，对一个数组进行求和的常见方法是：&nbsp;<br><font color=#008000>// sumarray.h</font><br><br>template &lt;typename T&gt;<br>inline T sum_array(int Dim, T* a)<br>{<br>&nbsp;&nbsp;&nbsp; T result = T();<br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; Dim; ++i)&nbsp;<br>&nbsp;&nbsp;&nbsp; {&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result += a[i];<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return result;<br>}<br>这当然可行，但我们也可以利用模板元编程技术来解开循环：&nbsp;<br><font color=#008000>// sumarray2.h<br><br>// 原始模板</font><br>template &lt;int Dim, typename T&gt;<br>class Sumarray&nbsp;<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static T result(T* a)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[0] + Sumarray&lt;Dim-1, T&gt;::result(a+1);<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br><font color=#008000>// 作为终结准则的局部特化版</font><br>template &lt;typename T&gt;<br>class Sumarray&lt;1, T&gt;&nbsp;<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; static T result(T* a)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[0];<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>用法如下：&nbsp;<br><br><font color=#008000>// sumarraytest2.cpp</font><br><br>#include &lt;iostream&gt;<br>#include "sumarray2.h"<br><br>int main()<br>{<br>&nbsp;&nbsp;&nbsp; int a[6] = {1, 2, 3, 4, 5, 6};<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; " Sumarray&lt;6&gt;(a) = " &lt;&lt; Sumarray&lt;6, int&gt;::result(a);<br>}<br>当我们计算Sumarray&lt;6, int&gt;::result(a)时，实例化过程如下：&nbsp;<br>Sumarray&lt;6, int&gt;::result(a)<br>= a[0] + Sumvector&lt;5, int&gt;::result(a+1)<br>= a[0] + a[1] + Sumvector&lt;4, int&gt;::result(a+2)<br>= a[0] + a[1] + a[2] + Sumvector&lt;3, int&gt;::result(a+3)<br>= a[0] + a[1] + a[2] + a[3] + Sumvector&lt;2, int&gt;::result(a+4)<br>= a[0] + a[1] + a[2] + a[3] + a[4] + Sumvector&lt;1, int&gt;::result(a+5)<br>= a[0] + a[1] + a[2] + a[3] + a[4] + a[5]&nbsp;<br>可见，循环被展开为a[0]&nbsp; + a[1] + a[2] + a[3] + a[4] + a[5]。这种直截了当的展开运算几乎总是比循环来得更有效率。&nbsp;<br>也许拿一个有着600万个元素的数组来例证循环开解的优势可能更有说服力。生成这样的数组很容易，有兴趣，你不妨测试、对比一下。&nbsp;<br><br><strong>模板元编程在数值计算程序库中的应用</strong>&nbsp;<br><br>Blitz++之所以&#8220;快如闪电&#8221;（这正是blitz的字面含义），离不开模板元程序的功劳。Blitz++淋漓尽致地使用了元编程技术，你可以到这些文件源代码中窥探究竟：&nbsp;<br>
<ul>
    <li>dot.h<br>
    <li>matassign.h<br>
    <li>matmat.h<br>
    <li>matvec.h<br>
    <li>metaprog.h <br>
    <li>&nbsp;product.h <br>
    <li>sum.h <br>
    <li>&nbsp;vecassign.h&nbsp;<br></li>
</ul>
让我们看看Blitz++程序库dot.h文件中的模板元程序：<br>&nbsp;<br>template&lt;int N, int I&gt;<br>class _bz_meta_vectorDot {<br>public:<br>&nbsp;&nbsp;&nbsp; enum { loopFlag = (I &lt; N-1) ? 1 : 0 };<br><br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class T_expr2&gt;<br>&nbsp;&nbsp;&nbsp; static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>&nbsp;&nbsp;&nbsp; f(const T_expr1&amp; a, const T_expr2&amp; b)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[I] * b[I] + _bz_meta_vectorDot&lt;loopFlag * N, loopFlag * (I+1)&gt;::f(a,b);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class T_expr2&gt;<br>&nbsp;&nbsp;&nbsp; static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>&nbsp;&nbsp;&nbsp; f_value_ref(T_expr1 a, const T_expr2&amp; b)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[I] * b[I] + _bz_meta_vectorDot&lt;loopFlag * N, loopFlag * (I+1)&gt;::f(a,b);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class T_expr2&gt;<br>&nbsp;&nbsp;&nbsp; static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, _bz_typename T_expr2::T_numtype)<br>&nbsp;&nbsp;&nbsp; f_ref_value(const T_expr1&amp; a, T_expr2 b)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[I] * b[I] + _bz_meta_vectorDot&lt;loopFlag * N, loopFlag * (I+1)&gt;::f(a,b);<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class P_numtype2&gt;<br>&nbsp;&nbsp;&nbsp; static inline BZ_PROMOTE(_bz_typename T_expr1::T_numtype, P_numtype2)<br>&nbsp;&nbsp;&nbsp; dotWithArgs(const T_expr1&amp; a, P_numtype2 i1, P_numtype2 i2=0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return a[I] * i1 + _bz_meta_vectorDot&lt;loopFlag * N, loopFlag * (I+1)&gt;::dotWithArgs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (a, i2, i3, i4, i5, i6, i7, i8, i9);<br>&nbsp;&nbsp;&nbsp; }<br>};<br><br>template&lt;&gt;<br>class _bz_meta_vectorDot&lt;0,0&gt; {<br>public:<br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class T_expr2&gt;<br>&nbsp;&nbsp;&nbsp; static inline _bz_meta_nullOperand f(const T_expr1&amp;, const T_expr2&amp;)<br>&nbsp;&nbsp;&nbsp; { return _bz_meta_nullOperand(); }<br><br>&nbsp;&nbsp;&nbsp; template&lt;class T_expr1, class P_numtype2&gt;<br>&nbsp;&nbsp;&nbsp; static inline _bz_meta_nullOperand&nbsp;<br>&nbsp;&nbsp;&nbsp; dotWithArgs(const T_expr1&amp; a, P_numtype2 i1, P_numtype2 i2=0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P_numtype2 i3=0, P_numtype2 i4=0, P_numtype2 i5=0, P_numtype2 i6=0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; P_numtype2 i7=0, P_numtype2 i8=0, P_numtype2 i9=0, P_numtype2 i10=0)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return _bz_meta_nullOperand();&nbsp;<br>&nbsp;&nbsp;&nbsp; }<br>};<br>这段代码远比它乍看上去的简单。_bz_meta_vectorDot类模板使用了一个临时变量loopFlag来存放每一步循环条件的评估结果，并使用了一个完全特化版作为递归终结的条件。需要说明的是，和几乎所有元程序一样，这个临时变量作用发挥于编译期，并将从运行代码中优化掉。&nbsp;<br>Todd是在Blitz++数值数组库的主要作者。这个程序库（以及MTL和POOMA等程序库）例证了模板元程序可以为我们带来更加高效的数值计算性能。Todd宣称Blitz++的性能可以和对应的Fortran程序库媲美。<br>&nbsp;<br><strong>Loki程序库：活用模板元编程技术的典范</strong>&nbsp;<br><br>模板元编程的价值仅仅在于高性能数值计算吗？不仅如此。Loki程序库以对泛型模式的开创性工作闻名于C++社群。它很巧妙地利用了模板元编程技术实现了Typelist组件。Typelist是实现Abstract Factory、Visitor等泛型模式不可或缺的基础设施。&nbsp;<br>就像C++标准库组件std::list提供对一组数值的操作一样，Typelist可以用来操纵一组类型，其定义非常简单（摘自Loki程序库Typelist.h单元）：&nbsp;<br>template &lt;class T, class U&gt;<br>struct Typelist<br>{<br>&nbsp;&nbsp;&nbsp; typedef T Head;<br>&nbsp;&nbsp;&nbsp; typedef U Tail;<br>};&nbsp;<br>显然，Typelist没有任何状态，也未定义任何操作，其作用只在于携带类型信息，它并未打算被实例化，因此，对于Typelist的任何处理都必然发生于编译期而非运行期。&nbsp;<br>Typelist可以被无限扩展，因为模板参数可以是任何类型（包括该模板的其他具现体）。例如：&nbsp;<br>Typelist&lt;char, Typelist&lt;int, Typelist&lt;float, NullType&gt; &gt; &gt; &nbsp;<br>就是一个包含有char、int、float三种类型的Typelist。&nbsp;<br>按照Loki的约定，每一个Typelist都必须以NullType结尾。NullType的作用类似于传统C字符串的&#8220;\0&#8221;，它被声明于Loki程序库的NullType.h文件中：&nbsp;<br>class NullType;&nbsp;<br>NullType只有声明，没有定义，因为Loki程序库永远都不需要创建一个NullType对象。&nbsp;<br>让我们看看IndexOf模板元程序，它可以在一个Typelist中查找给定类型的位置（摘自Loki程序库的Typelist.h单元）：&nbsp;<br>template &lt;class TList, class T&gt;<br>struct IndexOf;<br><br>template &lt;class T&gt;<br>struct IndexOf&lt;NullType, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; enum { value = -1 };<br>};<br><br>template &lt;class T, class Tail&gt;<br>struct IndexOf&lt;Typelist&lt;T, Tail&gt;, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; enum { value = 0 };<br>};<br><br>template &lt;class Head, class Tail, class T&gt;<br>struct IndexOf&lt;Typelist&lt;Head, Tail&gt;, T&gt;<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; enum { temp = IndexOf&lt;Tail, T&gt;::value };<br>public:<br>&nbsp;&nbsp;&nbsp; enum { value = (temp == -1 ? -1 : 1 + temp) };<br>};&nbsp;<br><br>IndexOf提供了一个原始模板和三个局部特化版。算法非常简单：如果TList（就是一个Typelist）是一个NullType，则value为-1。如果TList的头部就是T，则value为0。否则将IndexOf施行于TList的尾部和T，并将评估结果置于一个临时变量temp中。如果temp为-1，则value为-1，否则value为1 + temp。&nbsp;<br>为了加深你对Typelist采用的模板元编程技术的认识，我从Loki程序库剥离出如下代码，放入一个typelistlite.h文件中：&nbsp;<br><font color=#008000>// typelistlite.h<br><br>// 声明Nulltype&nbsp;</font><br>class NullType;<br><br><font color=#008000>// Typelist的定义</font><br>template &lt;class T, class U&gt;<br>struct Typelist<br>{<br>&nbsp;&nbsp;&nbsp; typedef T Head;<br>&nbsp;&nbsp;&nbsp; typedef U Tail;<br>};<br><br><font color=#008000>// IndexOf的定义&nbsp;<br><br>// IndexOf原始模板</font><br>template &lt;class TList, class T&gt; struct IndexOf;<br><br><font color=#008000>// 针对NullType的局部特化版</font><br>template &lt;class T&gt;<br>struct IndexOf&lt;NullType, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; enum { value = -1 };<br>};<br><br><font color=#008000>// 针对&#8220;Tlist头部就是我们要查找的T&#8221;的局部特化版</font><br>template &lt;class T, class Tail&gt;<br>struct IndexOf&lt;Typelist&lt;T, Tail&gt;, T&gt;<br>{<br>&nbsp;&nbsp;&nbsp; enum { value = 0 };<br>};<br><br><font color=#008000>// 处理Tlist尾部的局部特化版</font><br>template &lt;class Head, class Tail, class T&gt;<br>struct IndexOf&lt;Typelist&lt;Head, Tail&gt;, T&gt;<br>{<br>private:<br>&nbsp;&nbsp;&nbsp; enum { temp = IndexOf&lt;Tail, T&gt;::value };<br>public:<br>&nbsp;&nbsp;&nbsp; enum { value = (temp == -1 ? -1 : 1 + temp) };<br>};&nbsp;<br><br>测试程序如下：&nbsp;<br><br><font color=#008000>// typelistlite_test.cpp</font><br><br>#include &lt;iostream&gt;<br>#include "typelistlite.h"<br><br><font color=#008000>// 自定义类型Royal</font><br>class Royal {};<br><br><font color=#008000>// 定义一个包含有char、int、Royal和float的Typelist</font><br>typedef Typelist&lt;char, Typelist&lt;int, Typelist&lt;Royal, Typelist&lt;float, NullType&gt; &gt; &gt; &gt; CIRF;<br><br>int main()<br>{<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "IndexOf&lt;CIRF, int&gt;::value = " &lt;&lt; IndexOf&lt;CIRF, int&gt;::value &lt;&lt; "\n";<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "IndexOf&lt;CIRF, Royal&gt;::value = " &lt;&lt; IndexOf&lt;CIRF, Royal&gt;::value &lt;&lt; "\n";<br>&nbsp;&nbsp;&nbsp; std::cout &lt;&lt; "IndexOf&lt;CIRF, double&gt;::value = " &lt;&lt; IndexOf&lt;CIRF, double&gt;::value &lt;&lt; "\n";<br>}&nbsp;<br><br>程序输出如下：&nbsp;<br><br>IndexOf&lt;CIRF, int&gt;::value = 1<br>IndexOf&lt;CIRF, Royal&gt;::value = 2<br>IndexOf&lt;CIRF, double&gt;::value = -1&nbsp;<br><br><strong>结语</strong>&nbsp;<br><br>模板元编程技术并非都是优点，比方说，模板元程序编译耗时，带有模板元程序的程序生成的代码尺寸要比普通程序的大，而且通常这种程序调试起来也比常规程序困难得多。另外，对于一些程序员来说，以类模板的方式描述算法也许有点抽象。&nbsp;<br>编译耗时的代价换来的是卓越的运行期性能。通常来说，一个有意义的程序的运行次数（或服役时间）总是远远超过编译次数（或编译时间）。为程序的用户带来更好的体验，或者为性能要求严格的数值计算换取更高的性能，值得程序员付出这样的代价。&nbsp;<br>很难想象模板元编程技术会成为每一个普通程序员的日常工具，相反，就像Blitz++和Loki那样，模板元程序几乎总是应该被封装在一个程序库的内部。对于库的用户来说，它应该是透明的。模板元程序可以（也应该）用作常规模板代码的内核，为关键的算法实现更好的性能，或者为特别的目的实现特别的效果。&nbsp;<br>模板元编程技术首次正式亮相于Todd Veldhuizen的<em>Using C++ Template Metaprograms</em>论文之中。这篇文章首先发表于1995年5月的<em>C++ Report</em>期刊上，后来Stanley Lippman编辑<em>C++ Gems</em>一书时又收录了它。参考文献中给出了这篇文章的链接，它还描述了许多本文没有描述到的内容。&nbsp;<br>David Vandevoorde和Nicolai M. Josuttis合著的<em>C++ Templates: The Complete Guide</em>一书花了一整章的篇幅介绍模板元编程技术，它同样是本文的参考资料并且也应该作为你的补充阅读材料。&nbsp;<br>Andrei Alexandrescu的天才著作<em>Modern C++ Design: Generic Programming and Design Patterns Applied</em>的第3章<em>Typelists</em>对Typelist有着更为详尽的描述。<br>&nbsp;<br><strong>参考文献</strong>&nbsp;<br><br>1. David Vandevoorde, Nicolai M. Josuttis, <em>C++ Templates: The Complete Guide</em>, Addison Wesley, 2002<o:p>. <br>2.&nbsp; Andrei Alexandrescu, <em>Modern C++ Design: Generic Programming and Design Patterns Applied</em>, Addison Wesley, 2001.<br>3. 侯捷 於春景 译，《C++设计新思维》，华中科技大学出版社，2003。 <br>4. Todd Veldhuizen, <em>Template Metaprograms,</em> <a href="http://osl.iu.edu/~tveldhui/papers/Template-Metaprograms/meta-art.html" target=_blank><u><font color=#0000ff>http://osl.iu.edu/~tveldhui/papers/Template-Metaprograms/meta-art.html </font></u></a>. <br>5. Todd Veldhuizen, <em>C++ templates as partial evaluation</em> (PEPM99),&nbsp;<a href="http://osl.iu.edu/~tveldhui/papers/pepm99/"><u><font color=#0000ff>http://osl.iu.edu/~tveldhui/papers/pepm99/</font></u></a>. <br>6. Erwin Unruh, Prime numbers(Primzahlen - <a href="http://www.erwin-unruh.de/primorig.html"><u><font color=#0000ff>Original</font></u></a>), <a href="http://www.erwin-unruh.de/primorig.html"><u><font color=#0000ff>http://www.erwin-unruh.de/primorig.html </font></u></a>. <br>7. Erwin Unruh, Prime numbers(Primzahlen), <a href="http://www.erwin-unruh.de/Prim.html"><u><font color=#0000ff>http://www.erwin-unruh.de/Prim.html </font></u></a>. <br>8. Blitz++, <a href="http://www.oonumerics.org/blitz"><u><font color=#800080>http://www.oonumerics.org/blitz </font></u></a>. <br>9. Loki, <a href="http://sourceforge.net/projects/loki-lib"><u><font color=#0000ff>http://sourceforge.net/projects/loki-lib </font></u></a>. <br>10. POOMA, <a href="http://www.pooma.com/"><u><font color=#0000ff>http://www.pooma.com</font></u></a>. <br>11. MinGW - Minimalist GNU for Windows, <a href="http://sourceforge.net/projects/mingw"><u><font color=#0000ff>http://sourceforge.net/projects/mingw</font></u></a>. </o:p>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/100972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-11-15 14:25 <a href="http://www.cppblog.com/zhaoyg/archive/2009/11/15/100972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>钻石型继承中的虚函数问题</title><link>http://www.cppblog.com/zhaoyg/archive/2009/10/28/99648.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 28 Oct 2009 04:51:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/10/28/99648.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/99648.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/10/28/99648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/99648.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/99648.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 10pt">问题1：</span><br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Base1<br><img id=Codehighlighter1_12_157_Open_Image onclick="this.style.display='none'; Codehighlighter1_12_157_Open_Text.style.display='none'; Codehighlighter1_12_157_Closed_Image.style.display='inline'; Codehighlighter1_12_157_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_12_157_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_12_157_Closed_Text.style.display='none'; Codehighlighter1_12_157_Open_Image.style.display='inline'; Codehighlighter1_12_157_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_12_157_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_12_157_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f()<br><img id=Codehighlighter1_47_89_Open_Image onclick="this.style.display='none'; Codehighlighter1_47_89_Open_Text.style.display='none'; Codehighlighter1_47_89_Closed_Image.style.display='inline'; Codehighlighter1_47_89_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_47_89_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_47_89_Closed_Text.style.display='none'; Codehighlighter1_47_89_Open_Image.style.display='inline'; Codehighlighter1_47_89_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_47_89_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_47_89_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base1::f</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;g()<br><img id=Codehighlighter1_116_155_Open_Image onclick="this.style.display='none'; Codehighlighter1_116_155_Open_Text.style.display='none'; Codehighlighter1_116_155_Closed_Image.style.display='inline'; Codehighlighter1_116_155_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_116_155_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_116_155_Closed_Text.style.display='none'; Codehighlighter1_116_155_Open_Image.style.display='inline'; Codehighlighter1_116_155_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_116_155_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_116_155_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base1::g</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Base2&nbsp;:</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;Base1<br><img id=Codehighlighter1_194_340_Open_Image onclick="this.style.display='none'; Codehighlighter1_194_340_Open_Text.style.display='none'; Codehighlighter1_194_340_Closed_Image.style.display='inline'; Codehighlighter1_194_340_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_194_340_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_194_340_Closed_Text.style.display='none'; Codehighlighter1_194_340_Open_Image.style.display='inline'; Codehighlighter1_194_340_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_194_340_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_194_340_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f()<br><img id=Codehighlighter1_230_272_Open_Image onclick="this.style.display='none'; Codehighlighter1_230_272_Open_Text.style.display='none'; Codehighlighter1_230_272_Closed_Image.style.display='inline'; Codehighlighter1_230_272_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_230_272_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_230_272_Closed_Text.style.display='none'; Codehighlighter1_230_272_Open_Image.style.display='inline'; Codehighlighter1_230_272_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_230_272_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_230_272_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base2::f</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;g()<br><img id=Codehighlighter1_299_338_Open_Image onclick="this.style.display='none'; Codehighlighter1_299_338_Open_Text.style.display='none'; Codehighlighter1_299_338_Closed_Image.style.display='inline'; Codehighlighter1_299_338_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_299_338_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_299_338_Closed_Text.style.display='none'; Codehighlighter1_299_338_Open_Image.style.display='inline'; Codehighlighter1_299_338_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_299_338_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_299_338_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base2::g</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Base3:</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;Base1<br><img id=Codehighlighter1_377_523_Open_Image onclick="this.style.display='none'; Codehighlighter1_377_523_Open_Text.style.display='none'; Codehighlighter1_377_523_Closed_Image.style.display='inline'; Codehighlighter1_377_523_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_377_523_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_377_523_Closed_Text.style.display='none'; Codehighlighter1_377_523_Open_Image.style.display='inline'; Codehighlighter1_377_523_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_377_523_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_377_523_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;f()<br><img id=Codehighlighter1_413_455_Open_Image onclick="this.style.display='none'; Codehighlighter1_413_455_Open_Text.style.display='none'; Codehighlighter1_413_455_Closed_Image.style.display='inline'; Codehighlighter1_413_455_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_413_455_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_413_455_Closed_Text.style.display='none'; Codehighlighter1_413_455_Open_Image.style.display='inline'; Codehighlighter1_413_455_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_413_455_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_413_455_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base3::f</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;g()<br><img id=Codehighlighter1_482_521_Open_Image onclick="this.style.display='none'; Codehighlighter1_482_521_Open_Text.style.display='none'; Codehighlighter1_482_521_Closed_Image.style.display='inline'; Codehighlighter1_482_521_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_482_521_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_482_521_Closed_Text.style.display='none'; Codehighlighter1_482_521_Open_Image.style.display='inline'; Codehighlighter1_482_521_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_482_521_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_482_521_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Base3::g</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br><img id=Codehighlighter1_569_630_Open_Image onclick="this.style.display='none'; Codehighlighter1_569_630_Open_Text.style.display='none'; Codehighlighter1_569_630_Closed_Image.style.display='inline'; Codehighlighter1_569_630_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_569_630_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_569_630_Closed_Text.style.display='none'; Codehighlighter1_569_630_Open_Image.style.display='inline'; Codehighlighter1_569_630_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Derive&nbsp;:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Base2,&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Base3&nbsp;</span><span id=Codehighlighter1_569_630_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_569_630_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br><img id=Codehighlighter1_596_628_Open_Image onclick="this.style.display='none'; Codehighlighter1_596_628_Open_Text.style.display='none'; Codehighlighter1_596_628_Closed_Image.style.display='inline'; Codehighlighter1_596_628_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_596_628_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_596_628_Closed_Text.style.display='none'; Codehighlighter1_596_628_Open_Image.style.display='inline'; Codehighlighter1_596_628_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top></span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;g()&nbsp;</span><span id=Codehighlighter1_596_628_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_596_628_Open_Text><span style="COLOR: #000000">{&nbsp;cout&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Derive::g1</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000">&nbsp;endl;&nbsp;}</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000">;</span></div>
<p style="FONT-SIZE: 10pt">以上代码是在论坛中遇见的，当时我并没有立刻反应上来这是什么缘故，思索一阵后，才弄明白。<br>原来，在Derive中没有重写f函数，又因为当派生类没有重写基类的虚函数时，派生类对象对该虚函数的调用，将会调用其基类中的版本，而Derive又是多继承，于是在Derive继承时就不知道Base1中的虚函数表应该记录哪个版本的f函数，是Base2，还是Base3。<br>因为Derive中已重定义g函数，Base1的虚函数表记录的是Derive::g。</p>
<p style="FONT-SIZE: 10pt">==============================================================================<br><br>问题2：<br>代码来源&lt;effective C++&gt;2nd<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;Lottery<br>{<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;draw();<br>};<br><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;GraphicalObject&nbsp;<br>{<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;draw();<br>};<br><br></span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;LotterySimulation:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;Lottery,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;GraphicalObject&nbsp;<br>{<br>&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;没有声明draw</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">};<br><br>LotterySimulation&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">pls&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;LotterySimulation;<br><br>pls</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">draw();&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;错误!&nbsp;----&nbsp;二义</span></div>
<br><span style="FONT-SIZE: 10pt">因为LotterySimulation中存在两个名为draw的函数，于是调用存在二义性。同时，即便更改其中一个draw的访问性也不能避免这种二义性，因为改变一个类成员的访问权限不应该改变程序的含义。<br>对于如下代码，仍然存在二义性<br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">&nbsp;SpecialLotterySimulation:&nbsp;</span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">&nbsp;LotterySimulation&nbsp;<br>{<br></span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000">:<br>&nbsp;&nbsp;</span><span style="COLOR: #0000ff">virtual</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;draw();<br>};<br><br>pls&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000">&nbsp;SpecialLotterySimulation;<br><br>pls</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">draw();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;错误!&nbsp;&nbsp;还是有二义<br></span></div>
</span><span style="FONT-SIZE: 10pt">因为，pls的静态类型是LotterySimulation，而名字的查找是向上进行的，所以即便SpecialLotterySimulation中定义了一个draw，对pls来说他是不会查看SpecialLotterySimilation中的名称的。</span>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/99648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-10-28 12:51 <a href="http://www.cppblog.com/zhaoyg/archive/2009/10/28/99648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>{偶尔学习C++标准} 之 [对象的复制 - 复制构造与赋值操作符] </title><link>http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Wed, 07 Oct 2009 12:25:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/98041.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/98041.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/98041.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 对象的复制 - 复制构造与赋值操作符&nbsp;&nbsp;<a href='http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html'>阅读全文</a><img src ="http://www.cppblog.com/zhaoyg/aggbug/98041.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-10-07 20:25 <a href="http://www.cppblog.com/zhaoyg/archive/2009/10/07/98041.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[ 转 ] VC++，掀起你的盖头来——谈VC++对象模型</title><link>http://www.cppblog.com/zhaoyg/archive/2009/09/14/96157.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Mon, 14 Sep 2009 12:35:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/09/14/96157.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/96157.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/09/14/96157.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/96157.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/96157.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 非常不错的一篇文章。正文：--------------------------------------------------------（美）简  格雷程化&nbsp;&nbsp;&nbsp; 译排版美化：恋花蝶（http://blog.csdn.net/lanphaday）原文地址：http://blog.csdn.net/hellothere/archive/2006/02/27/6117...&nbsp;&nbsp;<a href='http://www.cppblog.com/zhaoyg/archive/2009/09/14/96157.html'>阅读全文</a><img src ="http://www.cppblog.com/zhaoyg/aggbug/96157.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-09-14 20:35 <a href="http://www.cppblog.com/zhaoyg/archive/2009/09/14/96157.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>初识"返回值优化"</title><link>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85658.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 24 May 2009 15:11:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85658.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/85658.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85658.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/85658.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/85658.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 10pt; COLOR: #000000">在&lt;C++编程思想&gt;2th中文版 第12.3.3.2节&#8220;返回值优化&#8221;中说，对于处理&#8220;return Integer(left.i + left.i);&#8221;这种的返回时，编译器直接在目的内存中创建，且因为不是创建局部对象，故可直接调用普通构造函数，而不需要复制构造函数；但，对于<br>&#8220;<br>Integer temp;<br>return temp;<br>&#8221;<br>这样的返回值形式，是需要调用复制构造函数来在目标内存中创建对象的。<br><br>我在VC2005中试了如下的小函数，</span><br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">X&nbsp;f()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;X&nbsp;one(</span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">);<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">return&nbsp;one;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">因为VC中默认情况下debug模式优化被禁止;release模式优化可用，所以在release模式下直接将one的定义目标内存中;debug则是调用复制构造在目标内存中构造<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">return&nbsp;X(4);&nbsp;</span><span style="COLOR: #008000">//</span><span style="COLOR: #008000">&nbsp;release&nbsp;&amp;&nbsp;debug&nbsp;都直接在目标内存中构造对象</span><span style="COLOR: #008000"><br></span><span style="COLOR: #000000">}<br><br></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;main()<br>{<br>&nbsp;&nbsp;&nbsp;X&nbsp;test&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;f();<br>}</span></div>
<br><span style="FONT-SIZE: 10pt; COLOR: #000000">对于，<br>Integer temp;<br>return temp;<br>这种形式，在VC2005中，如果没有禁用优化，则不要求复制构造函数可访问，也就是说复制构造函数都不会被调用。<br>但标准中说：&#8220;Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions must be respected as if the temporary object was created. [ Example: even if the copy constructor is not called, all the semantic restrictions, such as accessibility (clause 11), shall be satisfied.]&#8221;，所以还是保留复制构造函数的可访问性吧。<br><br>P.S. : 后来了解到VC中(或者说标准允许)对命名对象的返回采用&#8220;命名返回值优化(NRVO)&#8221;来进行优化，但是对于这种优化只有在某些编译器选项开启后才得以实现，至少VC是这样的。<br></span><br><span style="FONT-SIZE: 10pt; COLOR: #000000">2009.6.18 更新<br></span>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/85658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-05-24 23:11 <a href="http://www.cppblog.com/zhaoyg/archive/2009/05/24/85658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>{偶尔学习C++标准} 之 [初识临时对象生命期]</title><link>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85538.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 24 May 2009 07:53:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85538.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/85538.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/05/24/85538.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/85538.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/85538.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 临时对象生命期&nbsp;&nbsp;<a href='http://www.cppblog.com/zhaoyg/archive/2009/05/24/85538.html'>阅读全文</a><img src ="http://www.cppblog.com/zhaoyg/aggbug/85538.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-05-24 15:53 <a href="http://www.cppblog.com/zhaoyg/archive/2009/05/24/85538.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>09年之学习计划</title><link>http://www.cppblog.com/zhaoyg/archive/2009/05/17/83178.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Sun, 17 May 2009 04:43:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2009/05/17/83178.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/83178.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2009/05/17/83178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/83178.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/83178.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 10pt">其实我并不想写这个所谓的&#8220;学习计划&#8221;，可以说是有些害怕写这东西，因为我常常是&#8220;计划赶不上变化&#8221;，例如08年上半年说是要将C++学完，但后来因为英语四级的原因而不得不搁浅（毕竟四级不能小视呀），而后来又因为一些其他因素致使我直到今年4月份才学完C++。可以说，所谓&#8220;计划&#8221;就是对我的讽刺。希望这次不要再&#8220;讽刺&#8221;了，已经没时间了。<br><br>2009年之内要完成如下内容：<br>&nbsp;&nbsp; 初步学会&nbsp;&nbsp;&nbsp; SQL Server 2005<br>&nbsp;&nbsp; 初步学会&nbsp;&nbsp;&nbsp; VC++编程<br>&nbsp;&nbsp; 看完《深入详解VC++》<br>&nbsp;&nbsp; 看完《SQL Server 编程入门经典》<br>&nbsp;&nbsp; 看完《Effective C++》<br>&nbsp;&nbsp; 看完《Exceptional C++》<br>&nbsp;&nbsp; 多学些算法及数据结构<br>&nbsp;&nbsp; 尽量多看些《深入理解计算机系统》中的内容<br><br>但愿这次能按时完成任务。</span>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/83178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2009-05-17 12:43 <a href="http://www.cppblog.com/zhaoyg/archive/2009/05/17/83178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转贴]void及void指针深层探索</title><link>http://www.cppblog.com/zhaoyg/archive/2008/06/06/52312.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Thu, 05 Jun 2008 16:21:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2008/06/06/52312.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/52312.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2008/06/06/52312.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/52312.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/52312.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 10pt">1.概述 <br>　　许多初学者对C/C++语言中的void及void指针类型不甚理解，因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说，并详述void及void指针类型的使用方法与技巧。 <br>　　2.void的含义 <br>　　void的字面意思是&#8220;无类型&#8221;，void *则为&#8220;无类型指针&#8221;，void *可以指向任何类型的数据。 <br>　　void几乎只有&#8220;注释&#8221;和限制程序的作用，因为从来没有人会定义一个void变量，让我们试着来定义： <br><br>void a; <br>　　这行语句编译时会出错，提示&#8220;illegal use of type 'void'&#8221;。不过，即使void a的编译不会出错，它也没有任何实际意义。 <br>　　void真正发挥的作用在于： <br>　　（1） 对函数返回的限定； <br>　　（2） 对函数参数的限定。 <br>　　我们将在第三节对以上二点进行具体说明。 <br>　　众所周知，如果指针p1和p2的类型相同，那么我们可以直接在p1和p2间互相赋值；如果p1和p2指向不同的数据类型，则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 <br>　　例如： <br>float *p1; <br>int *p2; <br>p1 = p2; <br>　　其中p1 = p2语句会编译出错，提示&#8220;'=' : cannot convert from 'int *' to 'float *'&#8221;，必须改为： <br>p1 = (float *)p2; <br>　而void *则不同，任何类型的指针都可以直接赋值给它，无需进行强制类型转换： <br>void *p1; <br>int *p2; <br>p1 = p2; <br>　　但这并不意味着，void *也可以无需强制类型转换地赋给其它类型的指针。因为&#8220;无类型&#8221;可以包容&#8220;有类型&#8221;，而&#8220;有类型&#8221;则不能包容&#8220;无类型&#8221;。道理很简单，我们可以说&#8220;男人和女人都是人&#8221;，但不能说&#8220;人是男人&#8221;或者&#8220;人是女人&#8221;。下面的语句编译出错： <br>void *p1; <br>int *p2; <br>p2 = p1; <br>　　提示&#8220;'=' : cannot convert from 'void *' to 'int *'&#8221;。 <br>3.void的使用 <br>　　下面给出void关键字的使用规则： <br>　　规则一 如果函数没有返回值，那么应声明为void类型 <br>　　在C语言中，凡不加返回值类型限定的函数，就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如： <br>add ( int a, int b ) <br>{ <br>return a + b; <br>} <br>int main(int argc, char* argv[]) <br>{ <br>printf ( "2 + 3 = %d", add ( 2, 3) ); <br>} <br>　　程序运行的结果为输出： <br>　　2 + 3 = 5 <br>　　这说明不加返回值说明的函数的确为int函数。 <br>　　林锐博士《高质量C/C++编程》中提到：&#8220;C++语言有很严格的类型安全检查，不允许上述情况（指函数不加类型声明）发生&#8221;。可是编译器并不一定这么认定，譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确，所以不能寄希望于编译器会做严格的类型检查。 <br>　　因此，为了避免混乱，我们在编写C/C++程序时，对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值，一定要声明为void类型。这既是程序良好可读性的需要，也是编程规范性的要求。另外，加上void类型声明后，也可以发挥代码的&#8220;自注释&#8221;作用。代码的&#8220;自注释&#8221;即代码能自己注释自己。 <br>规则二 如果函数无参数，那么应声明其参数为void <br>　　在C++语言中声明一个这样的函数： <br>int function(void) <br>{ <br>return 1; <br>} <br>　　则进行下面的调用是不合法的： <br>function(2); <br>　　因为在C++中，函数参数为void的意思是这个函数不接受任何参数。 <br>　　我们在Turbo C 2.0中编译： <br>#include "stdio.h" <br>fun() <br>{ <br>return 1; <br>} <br>main() <br>{ <br>printf("%d",fun(2)); <br>getchar(); <br>} <br>　　编译正确且输出1，这说明，在C语言中，可以给无参数的函数传送任意类型的参数，但是在C++编译器中编译同样的代码则会出错。在C++中，不能向无参数的函数传送任何参数，出错提示&#8220;'fun' : function does not take 1 parameters&#8221;。 <br>　　所以，无论在C还是C++中，若函数不接受任何参数，一定要指明参数为void。 <br>　　规则三 小心使用void指针类型 <br>　　按照ANSI(American National Standards Institute)标准，不能对void指针进行算法操作，即下列操作都是不合法的： <br>void * pvoid; <br>pvoid++; //ANSI：错误 <br>pvoid += 1; //ANSI：错误 <br>//ANSI标准之所以这样认定，是因为它坚持：进行算法操作的指针必须是确定知道其指向数据类型大小的。 <br>//例如： <br>int *pint; <br>pint++; //ANSI：正确 <br>　　pint++的结果是使其增大sizeof(int)。 <br>　　但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定，它指定void *的算法操作与char *一致。 <br>因此下列语句在GNU编译器中皆正确： <br>pvoid++; //GNU：正确 <br>pvoid += 1; //GNU：正确 <br>　　pvoid++的执行结果是其增大了1。 <br>　　在实际的程序设计中，为迎合ANSI标准，并提高程序的可移植性，我们可以这样编写实现同样功能的代码： <br>void * pvoid; <br>(char *)pvoid++; //ANSI：正确；GNU：正确 <br>(char *)pvoid += 1; //ANSI：错误；GNU：正确 <br>　　GNU和ANSI还有一些区别，总体而言，GNU较ANSI更&#8220;开放&#8221;，提供了对更多语法的支持。但是我们在真实设计时，还是应该尽可能地迎合ANSI标准。 <br>　　规则四 如果函数的参数可以是任意类型指针，那么应声明其参数为void * <br>　　典型的如内存操作函数memcpy和memset的函数原型分别为： <br>void * memcpy(void *dest, const void *src, size_t len); <br>void * memset ( void * buffer, int c, size_t num ); <br>　　这样，任何类型的指针都可以传入memcpy和memset中，这也真实地体现了内存操作函数的意义，因为它操作的对象仅仅是一片内存，而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *，而是char *，那才叫真的奇怪了！这样的memcpy和memset明显不是一个&#8220;纯粹的，脱离低级趣味的&#8221;函数！ <br>下面的代码执行正确： <br>//示例：memset接受任意类型指针 <br>int intarray[100]; <br>memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0 <br>//示例：memcpy接受任意类型指针 <br>int intarray1[100], intarray2[100]; <br>memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1 <br>　　有趣的是，memcpy和memset函数返回的也是void *类型，标准库函数的编写者是多么地富有学问啊！ <br>　　规则五 void不能代表一个真实的变量 <br>　　下面代码都企图让void代表一个真实的变量，因此都是错误的代码： <br>void a; //错误 <br>function(void a); //错误 <br>　　void体现了一种抽象，这个世界上的变量都是&#8220;有类型&#8221;的，譬如一个人不是男人就是女人（还有人妖？）。 <br>　　void的出现只是为了一种抽象的需要，如果你正确地理解了面向对象中&#8220;抽象基类&#8221;的概念，也很容易理解void数据类型。正如不能给抽象基类定义一个实例，我们也不能定义一个void（让我们类比的称void为&#8220;抽象数据类型&#8221;）变量。 <br>4.总结 <br>　　小小的void蕴藏着很丰富的设计哲学，作为一名程序设计人员，对问题进行深一个层次的思考必然使我们受益匪浅。<br><br>
<p><font face=Verdana>来源：弘少&nbsp;&nbsp;&nbsp; <font face=Verdana color=#0000ff><a href="http://www.bansun.com/bbs/thread-4880-1-5.html"><u>http://www.bansun.com/bbs/thread-4880-1-5.html</u></a></font></font></p>
</span>
<img src ="http://www.cppblog.com/zhaoyg/aggbug/52312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2008-06-06 00:21 <a href="http://www.cppblog.com/zhaoyg/archive/2008/06/06/52312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转载]内存区划分、内存分配、常量存储区、堆、栈、自由存储区、全局区</title><link>http://www.cppblog.com/zhaoyg/archive/2008/05/30/51587.html</link><dc:creator>zhaoyg</dc:creator><author>zhaoyg</author><pubDate>Fri, 30 May 2008 05:25:00 GMT</pubDate><guid>http://www.cppblog.com/zhaoyg/archive/2008/05/30/51587.html</guid><wfw:comment>http://www.cppblog.com/zhaoyg/comments/51587.html</wfw:comment><comments>http://www.cppblog.com/zhaoyg/archive/2008/05/30/51587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/zhaoyg/comments/commentRss/51587.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/zhaoyg/services/trackbacks/51587.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.cppblog.com/zhaoyg/archive/2008/05/30/51587.html'>阅读全文</a><img src ="http://www.cppblog.com/zhaoyg/aggbug/51587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/zhaoyg/" target="_blank">zhaoyg</a> 2008-05-30 13:25 <a href="http://www.cppblog.com/zhaoyg/archive/2008/05/30/51587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>