⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 appendix_a.html

📁 制作本书的目的是为了方便大家的阅读。转载时请保持本电子书的完整性。 前言、条款2、16、21、44根据从Addison-Wesley出版社下载的开放条款翻译。条款26、27、28、45根据从Sc
💻 HTML
📖 第 1 页 / 共 2 页
字号:
<pre>std::toupper(c, L)</pre><p>意思和</p><pre>std::use_facet&lt;std::ctype&lt;char&gt; &gt;(L).toupper(c)</pre><p>相同(如果c是char类型)。不过,最小化调用use_facet的次数是值得的,因为它可能相当昂贵。</p><p>正如词典比较不能适合于所有应用一样,一个一个字符的大小写转换也不总是适合的。(例如,在德语里,小写字母“&szlig;”对应着大写序列“SS”。)但是,不幸的是,一个一个字符的大小写转换是我们拥有的全部。C和C++标准库都没有提供任何一次用于不止一个字符的字符串转换形式。如果你的目的不能接受这个限制,那么就已经在标准库的范围之外了。</p><h3>离题一下:另一个方面</h3><p>如果你已经熟悉C++里的区域设置,你可能想果用另一种方式进行字符串比较:collate方面的存在正好封装了排序的细节,而它有一个接口很像C库函数strcmp的成员函数。甚至有一个方便的特征:如果L是一个区域设置对象,你可以通过写L(x,y)而不是通过讨厌地调用use_facet然后调用collate成员函数来比较两个字符串。</p><p>“classic”区域设置有一个进行词典排序的collate方面,和字符串的operator&lt;做的一样,但其他区域设置进行任何种比较都是合适的。如果你的系统正好有一个对任何你感兴趣的语言进行忽略大小写比较的区域设置,你可以使用它。那个区域设置甚至可能做出比一个一个字符比较更智能化的事情!</p><p>不幸的是,这个建议,可能是真的,并不能帮助像我们这些没有那样的系统的人。或许有一天一套这样的区域设置可以标准化,但现在它们还没有。如果没有人为你写了一种忽略大小写的比较,你就必须亲自写它。</p><h3>忽略大小写字符串比较</h3><p>使用ctype,用忽略大小写字符比较构造忽略大小写字符串比较是很简单的。这个版本不是最优的,但至少它是正确的。它基本上使用和以前相同的技术:使用<code>lexicographical_compare</code>比较两个字符串,而且通过把两个字符都转换成大写来比较它们。不过,这次我们小心地使用区域设置对象而不是全局变量。(另外说一下,把两个字符都转化成大写不一定总是等于把两个字符都变成小写的结果:没有保证操作是可逆的。例如,在法语里,通常忽略大写字符上的重音标记。在法语区域设置中,toupper有理由是有损转换;它可以把“é”和“e”都转换成同样的大写字符,“E”。那么,在这样的区域设置里,使用toupper的忽略大小写比较将说“é”和“E”是等价字符,而tolower将说它们不是。哪个是正确的答案?或许是前者,但它取决于语言,取决于当地习惯,取决于你的应用程序。)</p><pre>struct lt_str_1	: public std::binary_function&lt;std::string, std::string, bool&gt; {	struct lt_char {		const std::ctype&lt;char&gt;&amp; ct;		lt_char(const std::ctype&lt;char&gt;&amp; c) : ct(c) {}		bool operator()(char x, char y) const {			return ct.toupper(x) &lt; ct.toupper(y);		}	};	std::locale loc;	const std::ctype&lt;char&gt;&amp; ct;	lt_str_1(const std::locale&amp; L = std::locale::classic())			: loc(L), ct(std::use facet&lt;std::ctype&lt;char&gt; &gt;(loc)) {}	bool operator()(const std::string&amp; x, const std::string&amp; y) const{		return std::lexicographical_compare(x.begin(), x.end(),						y.begin(), y.end(),						lt_char(ct));	}};</pre><p>这还不很好;它比应该的慢。问题是讨厌的和技术性的:我们在循环内调用toupper,而C++标准要求toupper是虚函数调用。一些优化器可能聪明得足以把虚函数开销移到循环之外,但是大多数不是。循环内的虚函数调用应该避免。</p><p>在这里,避免它不是很简单。你可能想到正确答案是ctype的另一个成员函数,</p><pre>const char* ctype&lt;char&gt;::toupper(char* f, char* l) const</pre><p>这改变了区间[f, l)内的字符大小写。不幸的是,这不完全是我们的目标的正确接口。使用它来比较两个字符串要求把两个字符串都拷贝到缓冲区,然后把缓冲区转化成大写。那些缓冲区从哪里来?它们不能是固定大小的数组(多大才足够大?),但动态数组需要昂贵的内存分配。</p><p>另一个解决方案是每次对一个字符进行大小写转换并缓存结果。这不是一个完全通用的解决方案——例如,如果你用的是32位UCS 4字符,它将完全不能工作。不过,如果你用char(大部分系统上是8位),在比较函数对象里维护一个256字节的大小写转换信息不是没有道理的。</p><pre>struct lt_str_2:	public std::binary_function&lt;std::string, std::string, bool&gt; {	struct lt_char {		const char* tab;		lt_char(const char* t) : tab(t) {}		bool operator()(char x, char y) const {			return tab[x - CHAR_MIN] &lt; tab[y - CHAR_MIN];		}	};	char tab[CHAR_MAX - CHAR_MIN + 1];	lt_str_2(const std::locale&amp; L = std::locale::classic()) {		const std::ctype&lt;char&gt;&amp; ct = std::use_facet&lt;std::ctype&lt;char&gt; &gt;(L);		for (int i = CHAR_MIN; i &lt;= CHAR_MAX; ++i)			tab[i - CHAR_MIN] = (char) i;		ct.toupper(tab, tab + (CHAR_MAX - CHAR_MIN + 1));	}	bool operator()(const std::string&amp; x, const std::string&amp; y) const {		return std::lexicographical_compare(x.begin(), x.end(),						y.begin(), y.end(),						lt_char(tab));	}};</pre><p>正如你看见的,lt_str_1和lt_str_2不是非常不同。前者有一个直接使用ctype方面的字符比较函数对象,而后者使用一张预先算好的大写转换表的字符比较函数对象。如果你建立lt_str_2函数对象,使用它比较一些短字符串,然后放弃它,可能会比较慢。不过,对任何实际的使用来说,lt_str_2将明显比lt_str_1快。在我的系统上差别不止两倍:用lt_str_1排序一个23,791个单词的list花费0.86秒,而用lt_str_2花费0.4秒。</p><p>我们从所有这些里学到了什么?</p><ul>	<li>忽略大小写的字符串类是错误的抽象层面。C++标准库中的泛型算法是由策略参数化的,而你应该利用这个事实。</li>	<li>词典字符串比较建立在字符比较之上。一旦你有了一个忽略大小写的字符比较函数对象,问题就解决了。(而且你可以把那个函数对象重用于比较其他类型的字符序列,比如vector&lt;char&gt;,或字符串表,或原始的C字符串。)</li>	<li>忽略大小写的字符比较比看起来难。除了在一个特定区域设置的场景之外,它没有意义,所以字符比较函数对象需要储存区域设置信息。如果关系到速度,你应该写避免重复调用昂贵的方面操作的函数对象。</li></ul><p>正确的忽略大小写比较花费了大量手段,但是你只须把它写一次。你或许不想考虑locale;大多数人不。(谁想在1990年考虑千年虫?)如果你依赖区域设置的代码正确了,那么你忽视区域设置的可能性将大于你写出消除了这个依赖性的代码。</p><hr /><p><sup><a id="Note1" href="#Note1Back">[1]</a></sup> 参见Andrei Alexandrescu在《C++ Report》2000年4月的专栏<a href="bibliography.html#bi19">[19]</a>。</p><p><sup><a id="Note2" href="#Note2Back">[2]</a></sup> 警告:use_facet是一个函数模板,它的模板参数值出现在返回类型,而不是任何实参。使用一个叫做<dfn>显式模板参数特化</dfn>的语言特性来调用它,而一些C++编译器尚未支持那个特性。如果你使用了一个不支持的编译器,你的库实现可能提供了变通办法,所以你可以用某种方式调用<code>use_facet</code>。</p></body></html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -