7.模板与泛型编程 Templates and Generic Programming

C++templates的最初发展动机很直接:让我们得以建立“类型安全”(type-safe)的容器如 vector,list和 map。然而当愈多人用上 templates,他们发现templates有能力完成愈多可能的变化。容器当然很好,但泛型编程(generic programming)——写出的代码和其所处理的对象类型彼此独立——更好。S T L算法如for_each,find和 merge就是这一类编程的成果。最终人们发现,C++template机制自身是一部完整的图灵机(Turing-complete):它可以被用来计算任何可计算的值。于是导出了模板元编程(template metaprogramming),创造出“在C++编译器内执行并于编译完成时停止执行”的程序。近来这些日子,容器反倒只成为C++template馅饼上的一小部分。然而尽管template的应用如此宽广,有一组核心观念一直支撑着所有基于template的编程。那些观念便是本章焦点。

本章无法使你变成一个专家级的template程序员,但可以使你成为一个比较好的template程序员。本章也会给你必要信息,使你能够扩展你的template编程,到达你所渴望的境界。

条款41:了解隐式接口和编译期多态 Understand implicit interfaces and compile-time polymorphism.

面向对象编程世界总是以显式接口(explicit interfaces)和运行期多态(runtimepolymorphism)解决问题。举个例子,给定这样(无甚意义)的class:

class Widget {
public:
	Widget();
	virtual ~Widget();
	virtual std::size_t size() const;
	virtual void normalize();
	void swap(Widget &other);	//见条款25
	...
};

和这样(也是无甚意义)的函数:

void doProcessing(Widget &w)
{
	if(w.size() > 10 && w != someNastyWidget) {
		Widget temp(w);
		temp.normalize();
		temp.swap(w);
	}
}

我们可以这样说doProcessing内的w:

■ 由于 w的类型被声明为 Widget,所以 w必须支持 Widget接口。我们可以在源码中找出这个接口(例如在Widget的.h文件中),看看它是什么样子,所以我称此为一个显式接口(explicit interface),也就是它在源码中明确可见。

■ 由于Widget的某些成员函数是virtual,w对那些函数的调用将表现出运行期多态(runtime polymorphism),也就是说将于运行期根据w的动态类型(见条款37)决定究竟调用哪一个函数。

Templates 及泛型编程的世界,与面向对象有根本上的不同。在此世界中显式接口和运行期多态仍然存在,但重要性降低。反倒是隐式接口(implicit interfaces)和编译期多态(compile-time polymorphism)移到前头了。若想知道那是什么,看看当我们将doProcessing从函数转变成函数模板(function template)时发生什么事:

template<typename T>
void doProcessing(T &w)
{
	if(w.size() > 10 && w != someNastyWidget) {
		T temp(w);
		temp.normalize();
		temp.swap(w);
	}
}

现在我们怎么说doProcessing内的w呢?

■ w必须支持哪一种接口,系由template中执行于w身上的操作来决定。本例看来w的类型T好像必须支持size,normalize和swap成员函数、copy构造函数(用以建立temp)、不等比较(inequality comparison,用来比较someNasty-Widget)。我们很快会看到这并非完全正确,但对目前而言足够真实。重要的是,这一组表达式(对此 template 而言必须有效编译)便是 T 必须支持的一组隐式接口(implicit interface)。

■ 凡涉及w的任何函数调用,例如operator&gt;和operator!=,有可能造成template具现化(instantiated),使这些调用得以成功。这样的具现行为发生在编译期。“以不同的template参数具现化function templates”会导致调用不同的函数,这便是所谓的编译期多态(compile-time polymorphism)。

纵使你从未用过templates,应该不陌生“运行期多态”和“编译期多态”之间的差异,因为它类似于“哪一个重载函数该被调用”(发生在编译期)和“哪一个virtual函数该被绑定”(发生在运行期)之间的差异。显式接口和隐式接口的差异就比较新颖,需要更多更贴近的说明和解释。

通常显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成。例如Widget class:

class Widget {
public:
	Widget();
	virtual ~Widget();
	virtual std::size_t size() const;
	virtual void normalize();
	void swap(Widget &other);	
};

其public接口由一个构造函数、一个析构函数、函数size,normalize,swap及其参数类型、返回类型、常量性(constnesses)构成。当然也包括编译器产生的copy构造函数和copy assignment操作符(见条款5)。另外也可以包括typedefs,以及如果你大胆违反条款22(令成员变量为private)而出现的public成员变量。

隐式接口就完全不同了。它并不基于函数签名式,而是由有效表达式(valid expressions)组成。再次看看doProcessing template一开始的条件:

template<typename T>
void doProcessing(T &w)
{
	if(w.size() > 10 && w != someNastyWidget) {
		...
	}
	//T(w的类型)的隐式接口看来好像有这些约束
}

■ 它必须提供一个名为size的成员函数,该函数返回一个整数值。

■ 它必须支持一个 operator!=函数,用来比较两个 T 对象。这里我们假设someNastyWidget的类型为T。

真要感谢操作符重载(operator overloading)带来的可能性,这两个约束都不需要满足。是的,T必须支持size成员函数,然而这个函数也可能从base class继承而得。这个成员函数不需返回一个整数值,甚至不需返回一个数值类型。就此而言,它甚至不需要返回一个定义有operator&gt;的类型!它唯一需要做的是返回一个类型为X的对象,而X对象加上一个int(10的类型)必须能够调用一个operator&gt;。这个 operator&gt;不需要非得取得一个类型为 X的参数不可,因为它也可以取得类型Y的参数,只要存在一个隐式转换能够将类型X的对象转换为类型Y的对象!

同样道理,T并不需要支持operator!=,因为以下这样也是可以的:operator!=接受一个类型为 X 的对象和一个类型为 Y 的对象,T 可被转换为X 而someNastyWidget的类型可被转换为Y,这样就可以有效调用operator!=。

(偷偷告诉你,以上分析并未考虑这样的可能性:operator&&被重载,从一个连接词改变为或许完全不同的某种东西,从而改变上述表达式的意义。)

当人们第一次以此种方式思考隐式接口,大多数的他们会感到头疼。但真的不需要阿司匹林来镇痛。隐式接口仅仅是由一组有效表达式构成,表达式自身可能看起来很复杂,但它们要求的约束条件一般而言相当直接又明确。例如以下条件式:

if(w.size() > 10 && w != someNastyWidget) ...

关于函数size,oper-ator&gt;,operator&&或operator!=身上的约束条件,我们很难就此说得太多,但整体确认表达式约束条件却很容易。if语句的条件式必须是个布尔表达式,所以无论涉及什么实际类型,无论"w.size()&gt;10&&w!=someNastyWidget"导致什么,它都必须与bool兼容。这是template doProcessing加诸于其类型参数(type parameter)T的隐式接口的一部分。doProcessing要求的其他隐式接口:copy构造函数、normalize和swap也都必须对T型对象有效。

加诸于 template 参数身上的隐式接口,就像加诸于 class 对象身上的显式接口一样真实,而且两者都在编译期完成检查。就像你无法以一种“与 class 提供之显式接口矛盾”的方式来使用对象(代码将通不过编译),你也无法在template中使用“不支持template所要求之隐式接口”的对象(代码一样通不过编译)。

请记住

■ classes和templates都支持接口(interfaces)和多态(polymorphism)。

■ 对classes而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生于运行期。

■ 对 template 参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过template具现化和函数重载解析(function overloading resolution)发生于编译期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
交叉编译libx加GTK+2.1时候用到库各个库文件的集合,不用花费时间网上一个一个下载,已经经过本人亲测试实测,还可以编译GTK+directFB 1.1 libs_for_gtk Dir: atk-1.25.2.tar.gz atk-1.26.0.tar.gz bigreqsproto-1.1.2.tar.bz2 cairo-1.8.6.tar.gz CPAN-1.94.tar.gz DirectFB-1.1.1.tar.gz expat-2.0.1.tar.gz expat-2.1.0.tar.gz ExtUtils-MakeMaker-6.20.tar.gz fontconfig-2.4.2.tar.gz fontconfig-2.6.0.tar.gz fontconfig-2.7.3.tar.gz freetype-2.3.5.tar.bz2 freetype-doc-2.3.6.tar.bz2 glib-2.12.1.tar.bz2 glib-2.14.4.tar.gz glib-2.18.2.tar.bz2 glibc-2.2.5.tar.bz2 gtk+-2.10.14.tar.bz2 gtk+-2.10.7.tar.bz2 gtk+-2.12.12.tar.bz2 inputproto-2.2.tar.bz2 jpegsrc.v6b.rar jpegsrc.v6b.tar.gz kbproto-1.0.6.tar.bz2 libpng-1.2.10.tar.bz2 libpng-1.2.32.tar.lzma libpthread-stubs_0.1.orig.tar.gz libtool-1.5.22.tar.gz libtool-2.4.2.tar.xz libX11-1.0.3.tar.bz2 libX11-1.2.2.tar.bz2 libX11-1.3.2.tar.gz libX11-1.5.0.tar.bz2 libXau-1.0.7.tar.bz2 libXau-1.0.8.tar.bz2 libxcb-1.3.tar.bz2 libxcb-1.4.tar.bz2 libxcb-1.8.1.tar.bz2 libXdmcp-1.0.3.tar.gz libXdmcp-1.1.2.tar.bz2 libXext-1.0.1.tar.bz2 libXfont-1.4.4.tar.bz2 libXft-2.1.12.tar.bz2 libXft-2.1.13.tar.bz2 libxml2-2.6.30.tar.gz libxml2-2.6.32.tar.gz libxslt-1.1.29.tar.gz libXt-1.0.6.tar.bz2 pango-1.16.4.tar.bz2 pango-1.18.2.tar.gz pango-1.22.1.tar.bz2 pango-1.24.4.tar.bz2 pixman-0.24.0.tar.bz2 poky-pinky-3.1.1.tgz stubs-soft.h tiff-3.8.2.tar.gz tiff-4.0.7.tar.gz tslib-1.4.tar.gz xcb-proto-1.7.1.tar.bz2 xcmiscproto-1.1.2.tar.bz2 xcmiscproto-1.2.2.tar.bz2 xextproto-7.2.1.tar.bz2 xorg-server-1.3.0.0.tar.bz2 xorg-server-1.4.2.tar.bz2 xproto-7.0.23.tar.bz2 xtrans-1.2.7.tar.bz2 zlib-1.2.3.rar zlib-1.2.3.tar.gz zlib-1.2.5.tar.gz
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值