﻿<?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++博客-never too late to learn</title><link>http://www.cppblog.com/dudi/</link><description>programming is understanding</description><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 20:22:44 GMT</lastBuildDate><pubDate>Tue, 09 Jun 2026 20:22:44 GMT</pubDate><ttl>60</ttl><item><title>关于l-value和r-value的详细讨论</title><link>http://www.cppblog.com/dudi/archive/2006/01/20/2926.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Fri, 20 Jan 2006 03:56:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/20/2926.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2926.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/20/2926.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2926.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2926.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: int f(); <br><br>f()=1; //这个不行，编译器提示不是lvalue，应该是因为返回的是tmp吧？ <br><br>class X {}; <br><br>X g(); <br><br>g() = X(); //这个为什么可以？效果是什么？<br>&nbsp;&nbsp;<a href='http://www.cppblog.com/dudi/archive/2006/01/20/2926.html'>阅读全文</a><img src ="http://www.cppblog.com/dudi/aggbug/2926.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-20 11:56 <a href="http://www.cppblog.com/dudi/archive/2006/01/20/2926.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于explicit关键字</title><link>http://www.cppblog.com/dudi/archive/2006/01/20/2925.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Fri, 20 Jan 2006 03:55:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/20/2925.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2925.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/20/2925.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2925.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2925.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">申明在使用单一操作符时，避免隐式转换，强行要求使用显示转换。<br>带单一参数的构造函数在缺省情况下隐含一个转换操作符，请看下面的代码：</p>
<p style="FONT-SIZE: 10pt">class C {<br>int i;<br>//...<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C(int i);//constructor and implicit conversion operator<br>//as well<br>};</p>
<p style="FONT-SIZE: 10pt">void f() {</p>
<p style="FONT-SIZE: 10pt">C c(0);</p>
<p style="FONT-SIZE: 10pt">c = 5; //将 5 隐式转换为 C 对象，然后赋值</p>
<p style="FONT-SIZE: 10pt">}</p>
<p style="FONT-SIZE: 10pt">编译器重新编辑上述例子代码，如下： </p>
<p style="FONT-SIZE: 10pt">//////////////////////////////////////////////////////////////////////////////////////////<br>//"c=5;" 被编译器转换成下面这个样子：<br>/////////////////////////////////////////////////////////////////////////////////////////</p>
<p style="FONT-SIZE: 10pt">C temp(5);// 实例化一个临时对象,<br>c = temp; // 用 = 赋值<br>temp.C::~C(); // temp 的析构函数被激活</p>
<p style="FONT-SIZE: 10pt">在很多情况下，这个转换是有意的，并且是正当的。但有时我们不希望进行这种自动的转换，例如：</p>
<p style="FONT-SIZE: 10pt">class String {<br>int size;<br>char *p;<br>//..<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String (int sz); //这里不希望进行隐式转换操作<br>};<br>void f ()<br>{<br>&nbsp;&nbsp;&nbsp; String s(10);</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; // 下面是一个程序员的编码；发生一个意想不到的转换：</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; s = 100; // 糟糕，100 被转换为一个 String，然后被赋值给 s<br>} </p>
<p style="FONT-SIZE: 10pt">为了避免这样的隐式转换，应该象下面这样显式声明该带单一参数的构造函数：</p>
<p style="FONT-SIZE: 10pt">class String {<br>int size;<br>char *p;<br>//..<br>public:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 不要隐式转换<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; explicit String (int sz); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String (const char *s, int size n = 0); // 隐式转换<br>};</p>
<p style="FONT-SIZE: 10pt">void f ()<br>{<br>&nbsp;&nbsp;&nbsp; String s(10);</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; s = 100; // 现在编译时出错；需要显式转换：</p>
<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp; s = String(100); // 好；显式转换<br>&nbsp;&nbsp;&nbsp; s = "st";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 好；此时允许隐式转换<br>}</p>
<img src ="http://www.cppblog.com/dudi/aggbug/2925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-20 11:55 <a href="http://www.cppblog.com/dudi/archive/2006/01/20/2925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解析#pragma指令 </title><link>http://www.cppblog.com/dudi/archive/2006/01/20/2924.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Fri, 20 Jan 2006 03:43:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/20/2924.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2924.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/20/2924.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2924.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2924.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">在所有的预处理指令中，#Pragma 指令可能是最复杂的了，它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。&nbsp; <br>其格式一般为: #Pragma Para&nbsp; <br>其中Para 为参数，下面来看一些常用的参数。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(1)message 参数。 Message 参数是我最喜欢的一个参数，它能够在编译信息输出窗&nbsp; <br>口中输出相应的信息，这对于源代码信息的控制是非常重要的。其使用方法为：&nbsp; <br>#Pragma message(&#8220;消息文本&#8221;)&nbsp; <br>当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。&nbsp; <br>当我们在程序中定义了许多宏来控制源代码版本的时候，我们自己有可能都会忘记有没有正确的设置这些宏，此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法&nbsp; <br>#ifdef _X86&nbsp; <br>#Pragma message(&#8220;_X86 macro activated!&#8221;)&nbsp; <br>#endif&nbsp; <br>当我们定义了_X86这个宏以后，应用程序在编译时就会在编译输出窗口里显示&#8220;_&nbsp; <br>X86 macro activated!&#8221;。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了&nbsp; <br>。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(2)另一个使用得比较多的pragma参数是code_seg。格式如：&nbsp; <br>#pragma code_seg( ["section-name"[,"section-class"] ] )&nbsp; <br>它能够设置程序中函数代码存放的代码段，当我们开发驱动程序的时候就会使用到它。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(3)#pragma once (比较常用）&nbsp; <br>只要在头文件的最开始加入这条指令就能够保证头文件被编译一次，这条指令实际上在VC6中就已经有了，但是考虑到兼容性并没有太多的使用它。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(4)#pragma hdrstop表示预编译头文件到此为止，后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度，但如果所有头文件都进行预编译又可能占太多磁盘空间，所以使用这个选项排除一些头文件。&nbsp; <br>有时单元之间有依赖关系，比如单元A依赖单元B，所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级，如果使用了#pragma package(smart_init) ，BCB就会根据优先级的大小先后编译。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体&nbsp; <br>外观的定义。&nbsp; </p>
<p style="FONT-SIZE: 10pt">(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )&nbsp; <br>等价于：&nbsp; <br>#pragma warning(disable:4507 34) // 不显示4507和34号警告信息&nbsp; <br>#pragma warning(once:4385) // 4385号警告信息仅报告一次&nbsp; <br>#pragma warning(error:164) // 把164号警告信息作为一个错误。&nbsp; <br>同时这个pragma warning 也支持如下格式：&nbsp; <br>#pragma warning( push [ ,n ] )&nbsp; <br>#pragma warning( pop )&nbsp; <br>这里n代表一个警告等级(1---4)。&nbsp; <br>#pragma warning( push )保存所有警告信息的现有的警告状态。&nbsp; <br>#pragma warning( push, n)保存所有警告信息的现有的警告状态，并且把全局警告&nbsp; <br>等级设定为n。&nbsp; <br>#pragma warning( pop )向栈中弹出最后一个警告信息，在入栈和出栈之间所作的&nbsp; <br>一切改动取消。例如：&nbsp; <br>#pragma warning( push )&nbsp; <br>#pragma warning( disable : 4705 )&nbsp; <br>#pragma warning( disable : 4706 )&nbsp; <br>#pragma warning( disable : 4707 )&nbsp; <br>//.......&nbsp; <br>#pragma warning( pop )&nbsp; <br>在这段代码的最后，重新保存所有的警告信息(包括4705，4706和4707)。&nbsp; <br>（7）pragma comment(...)&nbsp; <br>该指令将一个注释记录放入一个对象文件或可执行文件中。&nbsp; <br>常用的lib关键字，可以帮我们连入一个库文件。 </p>
<p style="FONT-SIZE: 10pt">(8)#pragma pack()&nbsp; <br>我们知道在VC中，对于想结构体Struct这样的类型，VC采用8字节对齐的方式，如果我们不想使用8字节对齐（在网络变成中经常需要这样），我们可以在结构体前面加上&nbsp; <br>#pragma pack(1)&nbsp; <br>struct&nbsp; <br>{&nbsp; <br>......&nbsp; <br>}&nbsp; <br>#pragma pack()&nbsp; </p>
<img src ="http://www.cppblog.com/dudi/aggbug/2924.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-20 11:43 <a href="http://www.cppblog.com/dudi/archive/2006/01/20/2924.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈typename</title><link>http://www.cppblog.com/dudi/archive/2006/01/18/2879.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Wed, 18 Jan 2006 08:17:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/18/2879.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2879.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/18/2879.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2879.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2879.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C++ 并不总是把 class 和 typename 视为等同的东西。有时你必须使用 typename。为了理解这一点，我们不得不讨论你会在一个 template（模板）中涉及到的两种名字。</p>
<p style="FONT-SIZE: 10pt">　　假设我们有一个函数的模板，它能取得一个 STL-compatible container（STL 兼容容器）中持有的能赋值给 ints 的对象。进一步假设这个函数只是简单地打印它的第二个元素的值。它是一个用糊涂的方法实现的糊涂的函数，而且就像我下面写的，它甚至不能编译，但是请将这些事先放在一边——有一种方法能发现我的愚蠢：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename C&gt; // print 2nd element in<br>void print2nd(const C&amp; container) // container;<br>{ <br>　// this is not valid C++!<br>　if (container.size() &gt;= 2) {<br>　　C::const_iterator iter(container.begin()); // get iterator to 1st element<br>　　++iter; // move iter to 2nd element<br>　　int value = *iter; // copy that element to an int<br>　　std::cout &lt;&lt; value; // print the int<br>　}<br>} </p>
<p style="FONT-SIZE: 10pt">　　我突出了这个函数中的两个 local variables（局部变量），iter 和 value。iter 的类型是 C::const_iterator，一个依赖于 template parameter（模板参数）C 的类型。一个 template（模板）中的依赖于一个 template parameter（模板参数）的名字被称为 dependent names（依赖名字）。当一个 dependent names（依赖名字）嵌套在一个 class（类）的内部时，我称它为 nested dependent name（嵌套依赖名字）。C::const_iterator 是一个 nested dependent name（嵌套依赖名字）。实际上，它是一个 nested dependent type name（嵌套依赖类型名），也就是说，一个涉及到一个 type（类型）的 nested dependent name（嵌套依赖名字）。</p>
<p style="FONT-SIZE: 10pt">　　print2nd 中的另一个 local variable（局部变量）value 具有 int 类型。int 是一个不依赖于任何 template parameter（模板参数）的名字。这样的名字以 non-dependent names（非依赖名字）闻名。（我想不通为什么他们不称它为 independent names（无依赖名字）。如果，像我一样，你发现术语 "non-dependent" 是一个令人厌恶的东西，你就和我产生了共鸣，但是 "non-dependent" 就是这类名字的术语，所以，像我一样，转转眼睛放弃你的自我主张。）</p>
<p style="FONT-SIZE: 10pt">　　nested dependent name（嵌套依赖名字）会导致解析困难。例如，假设我们更加愚蠢地以这种方法开始 print2nd：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename C&gt;<br>void print2nd(const C&amp; container)<br>{<br>　C::const_iterator * x;<br>　...<br>} </p>
<p style="FONT-SIZE: 10pt">　　这看上去好像是我们将 x 声明为一个指向 C::const_iterator 的 local variable（局部变量）。但是它看上去如此仅仅是因为我们知道 C::const_iterator 是一个 type（类型）。但是如果 C::const_iterator 不是一个 type（类型）呢？如果 C 有一个 static data member（静态数据成员）碰巧就叫做 const_iterator 呢？再如果 x 碰巧是一个 global variable（全局变量）的名字呢？在这种情况下，上面的代码就不是声明一个 local variable（局部变量），而是成为 C::const_iterator 乘以 x！当然，这听起来有些愚蠢，但它是可能的，而编写 C++ 解析器的人必须考虑所有可能的输入，甚至是愚蠢的。</p>
<p style="FONT-SIZE: 10pt">　　直到 C 成为已知之前，没有任何办法知道 C::const_iterator 到底是不是一个 type（类型），而当 template（模板）print2nd 被解析的时候，C 还不是已知的。C++ 有一条规则解决这个歧义：如果解析器在一个 template（模板）中遇到一个 nested dependent name（嵌套依赖名字），它假定那个名字不是一个 type（类型），除非你用其它方式告诉它。缺省情况下，nested dependent name（嵌套依赖名字）不是 types（类型）。（对于这条规则有一个例外，我待会儿告诉你。）</p>
<p style="FONT-SIZE: 10pt">　　记住这个，再看看 print2nd 的开头：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename C&gt;<br>void print2nd(const C&amp; container)<br>{<br>　if (container.size() &gt;= 2) {<br>　　C::const_iterator iter(container.begin()); // this name is assumed to<br>　　... // not be a type </p>
<p style="FONT-SIZE: 10pt">　　这为什么不是合法的 C++ 现在应该很清楚了。iter 的 declaration（声明）仅仅在 C::const_iterator 是一个 type（类型）时才有意义，但是我们没有告诉 C++ 它是，而 C++ 就假定它不是。要想转变这个形势，我们必须告诉 C++ C::const_iterator 是一个 type（类型）。我们将 typename 放在紧挨着它的前面来做到这一点：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename C&gt; // this is valid C++<br>void print2nd(const C&amp; container)<br>{<br>if (container.size() &gt;= 2) {<br>typename C::const_iterator iter(container.begin());<br>...<br>}<br>} </p>
<p style="FONT-SIZE: 10pt">　　通用的规则很简单：在你涉及到一个在 template（模板）中的 nested dependent type name（嵌套依赖类型名）的任何时候，你必须把单词 typename 放在紧挨着它的前面。（重申一下，我待会儿要描述一个例外。）</p>
<p style="FONT-SIZE: 10pt">　　typename 应该仅仅被用于标识 nested dependent type name（嵌套依赖类型名）；其它名字不应该用它。例如，这是一个取得一个 container（容器）和这个 container（容器）中的一个 iterator（迭代器）的 function template（函数模板）：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename C&gt; // typename allowed (as is "class")<br>void f(const C&amp; container, // typename not allowed<br>typename C::iterator iter); // typename required </p>
<p style="FONT-SIZE: 10pt">　　C 不是一个 nested dependent type name（嵌套依赖类型名）（它不是嵌套在依赖于一个 template parameter（模板参数）的什么东西内部的），所以在声明 container 时它不必被 typename 前置，但是 C::iterator 是一个 nested dependent type name（嵌套依赖类型名），所以它必需被 typename 前置。</p>
<p style="FONT-SIZE: 10pt">　　"typename must precede nested dependent type names"（&#8220;typename 必须前置于嵌套依赖类型名&#8221;）规则的例外是 typename 不必前置于在一个 list of base classes（基类列表）中的或者在一个 member initialization list（成员初始化列表）中作为一个 base classes identifier（基类标识符）的 nested dependent type name（嵌套依赖类型名）。例如：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename T&gt;<br>class Derived: public Base&lt;T&gt;::Nested { <br>　// base class list: typename not<br>　public: // allowed<br>　　explicit Derived(int x)<br>　　: Base&lt;T&gt;::Nested(x) // base class identifier in mem<br>　　{ <br>　　　// init. list: typename not allowed<br>　<br>　　　typename Base&lt;T&gt;::Nested temp; // use of nested dependent type<br>　　　... // name not in a base class list or<br>　　} // as a base class identifier in a<br>　　... // mem. init. list: typename required<br>}; </p>
<p style="FONT-SIZE: 10pt">　　这样的矛盾很令人讨厌，但是一旦你在经历中获得一点经验，你几乎不会在意它。</p>
<p style="FONT-SIZE: 10pt">　　让我们来看最后一个 typename 的例子，因为它在你看到的真实代码中具有代表性。假设我们在写一个取得一个 iterator（迭代器）的 function template（函数模板），而且我们要做一个 iterator（迭代器）指向的 object（对象）的局部拷贝 temp，我们可以这样做：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename IterT&gt;<br>void workWithIterator(IterT iter)<br>{<br>　typename std::iterator_traits&lt;IterT&gt;::value_type temp(*iter);<br>　...<br>} </p>
<p style="FONT-SIZE: 10pt">　　不要让 std::iterator_traits&lt;IterT&gt;::value_type 吓倒你。那仅仅是一个 standard traits class（标准特性类）的使用，用 C++ 的说法就是 "the type of thing pointed to by objects of type IterT"（&#8220;被类型为 IterT 的对象所指向的东西的类型&#8221;）。这个语句声明了一个与 IterT objects 所指向的东西类型相同的 local variable（局部变量）(temp)，而且用 iter 所指向的 object（对象）对 temp 进行了初始化。如果 IterT 是 vector&lt;int&gt;::iterator，temp 就是 int 类型。如果 IterT 是 list&lt;string&gt;::iterator，temp 就是 string 类型。因为 std::iterator_traits&lt;IterT&gt;::value_type 是一个 nested dependent type name（嵌套依赖类型名）（value_type 嵌套在 iterator_traits&lt;IterT&gt; 内部，而且 IterT 是一个 template parameter（模板参数）），我们必须让它被 typename 前置。</p>
<p style="FONT-SIZE: 10pt">　　如果你觉得读 std::iterator_traits&lt;IterT&gt;::value_type 令人讨厌，就想象那个与它相同的东西来代表它。如果你像大多数程序员，对多次输入它感到恐惧，那么你就需要创建一个 typedef。对于像 value_type 这样的 traits member names（特性成员名），一个通用的惯例是 typedef name 与 traits member name 相同，所以这样的一个 local typedef 通常定义成这样：</p>
<p style="FONT-SIZE: 10pt">template&lt;typename IterT&gt;<br>void workWithIterator(IterT iter)<br>{<br>　typedef typename std::iterator_traits&lt;IterT&gt;::value_type value_type;</p>
<p style="FONT-SIZE: 10pt">　value_type temp(*iter);<br>　...<br>} </p>
<p style="FONT-SIZE: 10pt">　　很多程序员最初发现 "typedef typename" 并列不太和谐，但它是涉及 nested dependent type names（嵌套依赖类型名）规则的一个合理的附带结果。你会相当快地习惯它。你毕竟有着强大的动机。你输入 typename std::iterator_traits&lt;IterT&gt;::value_type 需要多少时间？</p>
<p style="FONT-SIZE: 10pt">　　作为结束语，我应该提及编译器与编译器之间对围绕 typename 的规则的执行情况的不同。一些编译器接受必需 typename 时它却缺失的代码；一些编译器接受不许 typename 时它却存在的代码；还有少数的（通常是老旧的）会拒绝 typename 出现在它必需出现的地方。这就意味着 typename 和 nested dependent type names（嵌套依赖类型名）的交互作用会导致一些轻微的可移植性问题。</p>
<p style="FONT-SIZE: 10pt">　　Things to Remember</p>
<p style="FONT-SIZE: 10pt">　　&#183;在声明 template parameters（模板参数）时，class 和 typename 是可互换的。</p>
<p style="FONT-SIZE: 10pt">　　&#183;用 typename 去标识 nested dependent type names（嵌套依赖类型名），在 base class lists（基类列表）中或在一个 member initialization list（成员初始化列表）中作为一个 base class identifier（基类标识符）时除外。</p>
<img src ="http://www.cppblog.com/dudi/aggbug/2879.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-18 16:17 <a href="http://www.cppblog.com/dudi/archive/2006/01/18/2879.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于C++头文件</title><link>http://www.cppblog.com/dudi/archive/2006/01/18/2876.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Wed, 18 Jan 2006 07:59:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/18/2876.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2876.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/18/2876.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2876.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2876.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt">1、传统 C<br>#include &lt;assert.h&gt;　　　　//设定插入点<br>#include &lt;ctype.h&gt;　　　　 //字符处理<br>#include &lt;errno.h&gt;　　　　 //定义错误码<br>#include &lt;float.h&gt;　　　　 //浮点数处理<br>#include &lt;fstream.h&gt;　　　 //文件输入／输出<br>#include &lt;iomanip.h&gt;　　　 //参数化输入／输出<br>#include &lt;iostream.h&gt;　　　//数据流输入／输出<br>#include &lt;limits.h&gt;　　　　//定义各种数据类型最值常量<br>#include &lt;locale.h&gt;　　　　//定义本地化函数<br>#include &lt;math.h&gt;　　　　　//定义数学函数<br>#include &lt;stdio.h&gt;　　　　 //定义输入／输出函数<br>#include &lt;stdlib.h&gt;　　　　//定义杂项函数及内存分配函数<br>#include &lt;string.h&gt;　　　　//字符串处理<br>#include &lt;strstrea.h&gt;　　　//基于数组的输入／输出<br>#include &lt;time.h&gt;　　　　　//定义关于时间的函数<br>#include &lt;wchar.h&gt;　　　　 //宽字符处理及输入／输出<br>#include &lt;wctype.h&gt;　　　　//宽字符分类</p>
<p style="FONT-SIZE: 10pt">2、标准C++（同上的不再注释）<br>#include &lt;algorithm&gt;　　　 //STL 通用算法<br>#include &lt;bitset&gt;　　　　　//STL 位集容器<br>#include &lt;cctype&gt;<br>#include &lt;cerrno&gt;<br>#include &lt;clocale&gt;<br>#include &lt;cmath&gt;<br>#include &lt;complex&gt;　　　　 //复数类<br>#include &lt;cstdio&gt;<br>#include &lt;cstdlib&gt;<br>#include &lt;cstring&gt;<br>#include &lt;ctime&gt;<br>#include &lt;deque&gt;　　　　　 //STL 双端队列容器<br>#include &lt;exception&gt;　　　 //异常处理类<br>#include &lt;fstream&gt;<br>#include &lt;functional&gt;　　　//STL 定义运算函数（代替运算符）<br>#include &lt;limits&gt;<br>#include &lt;list&gt;　　　　　　//STL 线性列表容器<br>#include &lt;map&gt;　　　　　　 //STL 映射容器<br>#include &lt;iomanip&gt;<br>#include &lt;ios&gt;　　　　　　 //基本输入／输出支持<br>#include &lt;iosfwd&gt;　　　　　//输入／输出系统使用的前置声明<br>#include &lt;iostream&gt;<br>#include &lt;istream&gt;　　　　 //基本输入流<br>#include &lt;ostream&gt;　　　　 //基本输出流<br>#include &lt;queue&gt;　　　　　 //STL 队列容器<br>#include &lt;set&gt;　　　　　　 //STL 集合容器<br>#include &lt;sstream&gt;　　　　 //基于字符串的流<br>#include &lt;stack&gt;　　　　　 //STL 堆栈容器　　　　<br>#include &lt;stdexcept&gt;　　　 //标准异常类<br>#include &lt;streambuf&gt;　　　 //底层输入／输出支持<br>#include &lt;string&gt;　　　　　//字符串类<br>#include &lt;utility&gt;　　　　 //STL 通用模板类<br>#include &lt;vector&gt;　　　　　//STL 动态数组容器<br>#include &lt;cwchar&gt;<br>#include &lt;cwctype&gt;</p>
<p style="FONT-SIZE: 10pt">3、C99 增加<br>#include &lt;complex.h&gt;　　 //复数处理<br>#include &lt;fenv.h&gt;　　　　//浮点环境<br>#include &lt;inttypes.h&gt;　　//整数格式转换<br>#include &lt;stdbool.h&gt;　　 //布尔环境<br>#include &lt;stdint.h&gt;　　　//整型环境<br>#include &lt;tgmath.h&gt;　　　//通用类型数学宏</p>
<p style="FONT-SIZE: 10pt">&nbsp;</p>
<img src ="http://www.cppblog.com/dudi/aggbug/2876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-18 15:59 <a href="http://www.cppblog.com/dudi/archive/2006/01/18/2876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>typedef &amp;&amp; typename</title><link>http://www.cppblog.com/dudi/archive/2006/01/18/2875.html</link><dc:creator>LeiJun</dc:creator><author>LeiJun</author><pubDate>Wed, 18 Jan 2006 07:53:00 GMT</pubDate><guid>http://www.cppblog.com/dudi/archive/2006/01/18/2875.html</guid><wfw:comment>http://www.cppblog.com/dudi/comments/2875.html</wfw:comment><comments>http://www.cppblog.com/dudi/archive/2006/01/18/2875.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/dudi/comments/commentRss/2875.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/dudi/services/trackbacks/2875.html</trackback:ping><description><![CDATA[<p style="FONT-SIZE: 10pt"><font size=1><span style="FONT-SIZE: 10pt"><strong>typedef：</strong>用于定义类型<br></span><span style="FONT-SIZE: 10pt">1、为了简化、清晰。比如：<br></span><span style="FONT-SIZE: 10pt">vector*&gt; temp(10);<br>可以简化为<br>typedef list listnum;<br>typedef vector vectornum;<br>vectornum temp(10);</span><br><span style="FONT-SIZE: 10pt">2、定义指向成员的指针<br>class A<br>{<br>&nbsp;&nbsp;&nbsp;virtual sup() = 0;<br>}<br>typedef void (A::* pt)();<br>void f(A *a)<br>{<br>&nbsp;&nbsp;&nbsp;pt ptemp = &amp;A::sup;<br>}<br></span><br><span style="FONT-SIZE: 10pt"><strong>typename:<br></strong>template的含义有两个：<br>1、typename var_name，表示var_name的定义还没有给出，这个语句通常出现在模版的定义内。例如：<br></span></font><span style="FONT-SIZE: 10pt">template <br>void f()<br>{<br>&nbsp; typedef typename T::A TA;&nbsp;&nbsp;&nbsp;&nbsp;// 声明 TA 的类型为 T::A<br>&nbsp; TA a5;&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;// 声明 a5 的类型为 TA<br>&nbsp; typename T::A a6;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 声明 a6 的类型为 T::A<br>&nbsp; TA * pta6;&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; // 声明 pta6 的类型为 TA 的指针<br>}<br>因为T是一个模版实例化时才知道的类型，所以编译器更对T::A不知所云，为了通知编译器T::A是一个合法的类型，使用typename语句可以避免编译器报错。<br>2、template &lt; typename var_name &gt; class class_name，表示var_name是一个类型，在模版实例化时可以替换任意类型，不仅包括内置类型（int等），也包括自定义类型class。<br>这就是问题中的形式。换句话说，在template中，typename和class的意义完全一样。</span></p>
<img src ="http://www.cppblog.com/dudi/aggbug/2875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/dudi/" target="_blank">LeiJun</a> 2006-01-18 15:53 <a href="http://www.cppblog.com/dudi/archive/2006/01/18/2875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>