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

📄 item_40.html

📁 制作本书的目的是为了方便大家的阅读。转载时请保持本电子书的完整性。 前言、条款2、16、21、44根据从Addison-Wesley出版社下载的开放条款翻译。条款26、27、28、45根据从Sc
💻 HTML
字号:
<?xml version="1.0" encoding="gb2312"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>条款40:使仿函数类可适配</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><link href="css/all.css" rel="stylesheet" type="text/css" /><link rel="stylesheet" href="http://stl.winterxy.com/styles-site.css" type="text/css" /><link rel="alternate" type="application/rss+xml" title="RSS" href="http://stl.winterxy.com/index.rdf" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://stl.winterxy.com/rsd.xml" /></head><body><div id="banner"><h1><a href="http://stl.winterxy.com/" accesskey="1">Center of STL Study</a> </h1><span class="description">——最优秀的STL学习网站<script language="javascript" src="http://www.winterxy.com/cgi-bin/js/webstats.js"></script></span></div><h2>条款40:使仿函数类可适配</h2> <p>假设我有一个Widget*指针的list和一个函数来决定这样的指针是否确定一个有趣的Widget:</p> <pre>list&lt;Widget*&gt; widgetPtrs;bool isInteresting(const Widget *pw);</pre> <p>如果我要在list中找第一个指向有趣的Widget的指针,这很简单:</p> <pre>list&lt;Widget*&gt;::iterator i = find_if(widgetPtrs.begin(), widgetPtrs.end(),						isInteresting);if (i != widgetPtrs.end()) {	...							// 处理第一个}								// 有趣的指向								// Widget的指针</pre> <p>但如果我想要找第一个指向不有趣的Widget的指针,显而易见的方法却编译失败:</p> <pre>list&lt;Widget*&gt;::iterator i =	find_if(widgetPtrs.begin(), widgetPtrs.end(),			not1(isInteresting));			// 错误!不能编译</pre> <p>取而代之的是,我必须对isInteresting应用ptr_fun在应用not1之前:</p> <pre>list&lt;Widget*&gt;::iterator i =	find_if(widgetPtrs.begin(), widgetPtrs.end(),			not1(ptr_func(isInteresting)));		// 没问题if (i != widgetPtrs.end()) {	...							// 处理第一个}								// 指向Widget的指针</pre> <p>那会引出一些问题。为什么我必须在应用not1前对isInteresting应用ptr_fun?ptr_fun为我做了什么,怎么完成上面的工作的?</p> <p>答案多少有些令人惊讶。ptr_fun做的唯一的事是使一些typedef有效。就是这样。not1需要这些typedef,这就是为什么可以把not1应用于ptr_fun,但不能直接对isInteresting应用not1。因为是低级的函数指针,isInteresting缺乏not1需要的typedef。</p> <p>not1不是STL中唯一有那些要求的组件。四个标准函数适配器(not1、not2、bind1st和bind2nd)都需要存在某些typedef,一些其他人写的非标准STL兼容的适配器(比如来自SGI和Boost的——参见<a href="item_50.html">条款50</a>)也需要。提供这些必要的typedef的函数对象称为<dfn>可适配</dfn>的,而缺乏那些typedef的函数对象不可适配。可适配的比不可适配的函数对象可以用于更多的场景,所以只要能做到你就应该使你的函数对象可适配。这不花费你任何东西,而它可以为你仿函数类的客户购买一个便利的世界。</p> <p>我知道,我知道。我在卖弄,经常提及“某些typedef”而没有告诉你是什么。问题中的typedef是argument_type、first_argument_type、second_argument_type和result_type,但不是那么直截了当,因为不同类型仿函数类需要提供那些名字的不同子集。总的来说,除非你在写你自己的适配器(本书没有覆盖的主题),你才不需要知道任何关于那些typedef的事情。那是因为提供它们的正规方法是从一个基类,或,更精确地说,一个基结构,继承它们。operator()带一个实参的仿函数类,要继承的结构是std::unary_function。operator()带有两个实参的仿函数类,要继承的结构是std::binary_function。</p> <p>好,简单来说,unary_function和binary_function是模板,所以你不能直接继承它们。取而代之的是,你必须从它们产生的类继承,而那就需要你指定一些类型实参。对于unary_function,你必须指定的是由你的仿函数类的operator()所带的参数的类型和它的返回类型。对于binary_function,你要指定三个类型:你的operator的第一个和第二个参数的类型,和你的operator地返回类型。</p> <p>这里有两个例子:</p> <pre>template&lt;typename T&gt;class MeetsThreshold: public std::unary_function&lt;Widget, bool&gt;{private:	const T threshold;public:	MeetsThreshold(const T&amp; threshold);	bool operator()(const Widget&amp;) const;	...};struct WidgetNameCompare:	public std::binary_function&lt;Widget, Widget, bool&gt;{	bool operator()(const Widget&amp; lhs, const Widget&amp; rhs) const;};</pre> <p>在两种情况下,注意传给unary_function或binary_function的类型与传给仿函数类的operator()和从那里返回的一样,虽然operator的返回类型作为最后一个实参被传递给unary_function或binary_function有一点古怪。</p> <p>你可能注意到了MeetsThreshold是一个类,而WidgetNameCompare是一个结构。MeetsThreshold有内部状态(它的阈值数据成员),而类是封装那些信息的合理方法。WidgetNameCompare没有状态,因此不需要任何private的东西。所有东西都是public的仿函数类的作者经常把它们声明为struct而不是class,也许只因为可以避免在基类和operator()函数前面输入“public”。把这样的仿函数声明为class还是struct纯粹是一个个人风格问题。如果你仍然在精炼你的个人风格,想找一些仿效的对象,看看无状态STL自己的仿函数类(比如,less&lt;T&gt;、plus&lt;T&gt;等)一般写为struct。再看看WidgetNameCompare:</p> <pre>struct WidgetNameCompare:	public std::binary_function&lt;Widget, Widget, bool&gt; {	bool operator()(cost Widget&amp; lhs, const Widget&amp; rhs) const;}</pre> <p>虽然operator的实参类型是const Widget&amp;,但传给binary_function的是Widget。一般来说,传给unary_function或binary_function的非指针类型都去掉了const和引用。(不要问为什么。理由不很好也不很有趣。如果你真的想知道,写一些没有去掉它们的程序,然后去解剖编译器诊断结果。如果完成了这步,你<em>仍然</em>对这个问题感兴趣,访问<a href="http://www.boost.org">boost.org</a>(参见<a href="item_50.html">条款50</a>)然后看看他们关于特性和函数对象适配器的工作。)</p> <p>当operator()的参数是指针时这个规则变了。这里有一个和WidgetNameCompare相似的结构,但这个使用Widget*指针:</p> <pre>struct PtrWidgetNameCompare:	public std::binary_function&lt;const Widget*, const Widget*, bool&gt; {	bool operator()(const Widget* lhs, const Widget* rhs) const;};</pre> <p>在这里,传给binary_function的类型和operator()所带的类型<em>一样</em>。用于带有或返回指针的仿函数的一般规则是传给unary_function或binary_function的类型是operator()带有或返回的类型。</p> <p>不要忘记所有使用这些unary_function和binary_function基类基本理由的冗繁的文字。这些类提供函数对象适配器需要的typedef,所以从那些类继承产生可适配的函数对象。那使我们这么做:</p><pre>list&lt;Widget&gt; widgets;...list&lt;Widget&gt;::reverse_iterator i1 =				// 找到最后一个不	find_if(widgets.rbegin(), widgets.rend(),		// 适合阈值10的widget			not1(MeetsThreshold&lt;int&gt;(10)));	// (不管意味着什么)Widget w(<em>构造函数实参</em>);list&lt;Widget&gt;::iterator i2 =					// 找到第一个在由	find_if(widgets.begin(), widgets.end(),		// WidgetNameCompare定义			bind2nd(WidgetNameCompare(), w);	// 的排序顺序上先于w的widget</pre> <p>如果我们没有把仿函数类继承自unary_function或binary_function,这些例子都不能编译,因为not1和bind2nd都只和可适配的函数对象合作。</p> <p>STL函数对象模仿了C++函数,而一个C++函数只有一套参数类型和一个返回类型。结果,STL暗中假设每个仿函数类只有一个operator()函数,而且这个函数的参数和返回类型要被传给unary_function或binary_function(与我们刚讨论过的引用和指针类型的规则一致)。这意味着,虽然可能很诱人,但你不能通过建立一个单独的含有两个operator()函数的struct试图组合WidgetNameCompare和PtrWidgetNameCompare的功能。如果你那么做了,这个仿函数可能可以和最多一种它的调用形式(你传参数给binary_function的那个)适配,而一个只能一半适配的仿函数可能只比完全不能适配要好。</p> <p>有时候有必要给一个仿函数类多个调用形式(因此得放弃可适配性),<a href="item_07.html">条款7</a>、<a href="item_20.html">20</a>、<a href="item_23.html">23</a>和<a href="item_25.html">25</a>给了这种情况的例子。但是那种仿函数类是例外,不是规则。可适配性是重要的,每次你写仿函数类时都应该努力促进它。</p> </body></html>

⌨️ 快捷键说明

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