我已经用C++编程了几年,我已经使用了很多STL并且已经创建了我自己的模板类几次以了解它是如何完成的.
现在我正在尝试将模板更深入地集成到我的OO设计中,一个唠叨的想法不断回到我身边:它们只是一个宏,真的......你可以使用#defines实现(而不是UGLY)auto_ptrs,如果你真的想要.
这种思考模板的方式有助于我理解我的代码将如何实际工作,但我觉得我必须以某种方式忽略这一点.宏是邪恶的化身,但"模板元编程"风靡一时.
那么,真正的区别是什么?模板如何避免#define引导你进入的危险,比如
在您不期望它们的地方难以理解的编译器错误?
代码臃肿?
跟踪代码有困难吗?
设置调试器断点?
Ferruccio.. 50
宏是一种文本替换机制.
模板是一种功能性的图灵完备语言,在编译时执行并集成到C++类型系统中.您可以将它们视为该语言的插件机制.
宏是一种文本替换机制.
模板是一种功能性的图灵完备语言,在编译时执行并集成到C++类型系统中.您可以将它们视为该语言的插件机制.
这里有很多评论试图区分宏和模板.
是的 - 它们都是一样的:代码生成工具.
宏是一种原始形式,没有太多的编译器实施(比如在C中做对象 - 它可以完成,但它并不漂亮).模板更高级,并且有更好的编译器类型检查,错误消息等.
但是,每个人都有其他人没有的优势.
模板只能生成动态类类型 - 宏几乎可以生成您想要的任何代码(除了另一个宏定义).宏可以非常有用地将结构化数据的静态表嵌入到代码中.
另一方面,模板可以完成一些真正的FUNKY事情,这是宏不可能实现的.例如:
templateclass Unit { double value; public: Unit(double n) { value = n; } Unit operator+(Unit n) { return Unit (value + n.value); } Unit operator-(Unit n) { return Unit (value - n.value); } Unit operator*(double n) { return Unit (value * n); } Unit operator/(double n) { return Unit (value / n); } Unit operator*(Unit n) { return Unit (value * n.value); } Unit operator/(Unit n) { return Unit (value / n.value); } etc.... }; #define Distance Unit<1,0> #define Time Unit<0,1> #define Second Time(1.0) #define Meter Distance(1.0) void foo() { Distance moved1 = 5 * Meter; Distance moved2 = 10 * Meter; Time time1 = 10 * Second; Time time2 = 20 * Second; if ((moved1 / time1) == (moved2 / time2)) printf("Same speed!"); }
该模板允许编译器动态地动态创建和使用模板的类型安全实例.编译器实际上在编译时执行模板参数数学运算,在每个唯一结果所需的位置创建单独的类.隐含的单位<1,-1>(距离/时间=速度)类型是在条件中创建和比较的,但从未在代码中明确声明.
显然,大学里的某个人已经定义了40多个参数的模板(需要参考),每个参数代表不同的物理单位类型.考虑一下这类课程的类型安全性,仅针对您的数字.
它们由编译器解析,而不是由编译器之前运行的预处理器解析.
这是MSDN所说的:http: //msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx
以下是宏的一些问题:
编译器无法验证宏参数是否为兼容类型.
扩展宏而不进行任何特殊类型检查.
i和j参数被评估两次.例如,如果任一参数具有后递增变量,则递增执行两次.
由于宏由预处理器扩展,因此编译器错误消息将引用扩展宏,而不是宏定义本身.此外,宏将在调试期间以扩展形式显示.
如果这对你来说还不够,我不知道是什么.
答案是这么久,我不能总结一切,但:
例如,宏在功能模板没有确保类型安全时:编译器无法验证宏参数是否兼容类型 - 在实例化函数模板时编译器知道是否定义int
或float
定义operator +
模板为元编程打开了大门(简而言之,评估事物并在编译时做出决定):在编译时,可以知道类型是整数还是浮点; 它是一个指针还是它的const限定等... 在即将到来的c ++ 0x中看到"type traits"
类模板具有部分特化
函数模板具有明确的完全特化,在您的示例中add
可以实现add
与宏不可能实现的不同
宏没有任何范围
#define min(i, j) (((i) < (j)) ? (i) : (j))
- i
和j
参数评估两次.例如,如果任一参数具有后递增变量,则递增执行两次
因为宏是由预处理器扩展的,编译器错误消息将引用扩展宏,而不是宏定义本身.此外,宏将在调试期间以扩展形式显示
等等...
注意:在极少数情况下,我更倾向于依赖可变参数宏,因为在c ++ 0x成为主流之前不存在可变参数模板. C++ 11现场直播.
参考文献:
C++ FAQ Lite:[35]模板
模板的优点
模板与宏(C++)
在最基本的层面上,是的,模板只是宏替换.但是,通过这种方式思考,你就会跳过很多东西.
考虑模板专业化,据我所知,你不能用宏来模拟.这不仅允许某些类型的特殊实现,它还是模板元编程中的关键部分之一:
templatestruct is_void { static const bool value = false; } template <> struct is_void { static const bool value = true; }
这本身就是你能做的很多事情的一个例子.模板本身是图灵完备的.
这忽略了非常基本的东西,例如范围,类型安全性,以及宏的问题.
不.一个简单的反例:模板遵循命名空间,宏的忽略命名空间(因为它们是预处理器语句).
namespace foo { templateNumberType add(NumberType a, NumberType b) { return a+b; } #define ADD(x, y) ((x)+(y)) } // namespace foo namespace logspace { // no problemo template NumberType add(NumberType a, NumberType b) { return log(a)+log(b); } // redefintion: warning/error/bugs! #define ADD(x, y) (log(x)+log(y)) } // namespace logspace
C++模板有点像Lisp宏(不是C宏),因为它们在已经解析的代码版本上运行,它们允许您在编译时生成任意代码.不幸的是,你正在编写类似于原始Lambda演算的东西,所以像循环这样的高级技术有点麻烦.有关所有血腥细节,请参阅Krysztof Czarnecki和Ulrich Eisenecker的Generative Programming.
如果您正在寻找对该主题的更深入的处理,我可以将您转向每个人最喜欢的C++仇恨.这个人知道并且讨厌比我梦想的更多的C++.这同时使FQA令人难以置信的炎症和优秀的资源.
模板是类型安全的.
模板化对象/类型可以是命名空间,是类的私有成员等.
模板化函数的参数不会在整个函数体中复制.
这些确实是一个大问题,可以防止大量的错误.