Rod*_*ddy 54 c++ macros templates
我已经用C++编程了几年,我已经使用了很多STL并且已经创建了我自己的模板类几次以了解它是如何完成的.
现在我正在尝试将模板更深入地集成到我的OO设计中,一个唠叨的想法不断回到我身边:它们只是一个宏,真的......你可以使用#defines实现(而不是UGLY)auto_ptrs,如果你真的想要.
这种思考模板的方式有助于我理解我的代码将如何实际工作,但我觉得我必须以某种方式忽略这一点.宏是邪恶的化身,但"模板元编程"风靡一时.
那么,真正的区别是什么?模板如何避免#define引导你进入的危险,比如
Fer*_*cio 50
宏是一种文本替换机制.
模板是一种功能性的图灵完备语言,在编译时执行并集成到C++类型系统中.您可以将它们视为该语言的插件机制.
Jef*_*f B 31
这里有很多评论试图区分宏和模板.
是的 - 它们都是一样的:代码生成工具.
宏是一种原始形式,没有太多的编译器实施(比如在C中做对象 - 它可以完成,但它并不漂亮).模板更高级,并且有更好的编译器类型检查,错误消息等.
但是,每个人都有其他人没有的优势.
模板只能生成动态类类型 - 宏几乎可以生成您想要的任何代码(除了另一个宏定义).宏可以非常有用地将结构化数据的静态表嵌入到代码中.
另一方面,模板可以完成一些真正的FUNKY事情,这是宏不可能实现的.例如:
template<int d,int t> class Unit
{
double value;
public:
Unit(double n)
{
value = n;
}
Unit<d,t> operator+(Unit<d,t> n)
{
return Unit<d,t>(value + n.value);
}
Unit<d,t> operator-(Unit<d,t> n)
{
return Unit<d,t>(value - n.value);
}
Unit<d,t> operator*(double n)
{
return Unit<d,t>(value * n);
}
Unit<d,t> operator/(double n)
{
return Unit<d,t>(value / n);
}
Unit<d+d2,t+t2> operator*(Unit<d2,t2> n)
{
return Unit<d+d2,t+t2>(value * n.value);
}
Unit<d-d2,t-t2> operator/(Unit<d2,t2> n)
{
return Unit<d-d2,t-t2>(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!");
}
Run Code Online (Sandbox Code Playgroud)
该模板允许编译器动态地动态创建和使用模板的类型安全实例.编译器实际上在编译时执行模板参数数学运算,在每个唯一结果所需的位置创建单独的类.隐含的单位<1,-1>(距离/时间=速度)类型是在条件中创建和比较的,但从未在代码中明确声明.
显然,大学里的某个人已经定义了40多个参数的模板(需要参考),每个参数代表不同的物理单位类型.考虑一下这类课程的类型安全性,仅针对您的数字.
rle*_*lut 30
它们由编译器解析,而不是由编译器之前运行的预处理器解析.
这是MSDN所说的:http: //msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx
以下是宏的一些问题:
- 编译器无法验证宏参数是否为兼容类型.
- 扩展宏而不进行任何特殊类型检查.
- i和j参数被评估两次.例如,如果任一参数具有后递增变量,则递增执行两次.
- 由于宏由预处理器扩展,因此编译器错误消息将引用扩展宏,而不是宏定义本身.此外,宏将在调试期间以扩展形式显示.
如果这对你来说还不够,我不知道是什么.
Gre*_*osz 22
答案是这么久,我不能总结一切,但:
int
或float
定义operator +
add<float>(5, 3);
可以实现add<int>(5, 3);
与宏不可能实现的不同#define min(i, j) (((i) < (j)) ? (i) : (j))
- i
和j
参数评估两次.例如,如果任一参数具有后递增变量,则递增执行两次注意:在极少数情况下,我更倾向于依赖可变参数宏,因为在c ++ 0x成为主流之前不存在可变参数模板. C++ 11现场直播.
参考文献:
GMa*_*ckG 12
在最基本的层面上,是的,模板只是宏替换.但是,通过这种方式思考,你就会跳过很多东西.
考虑模板专业化,据我所知,你不能用宏来模拟.这不仅允许某些类型的特殊实现,它还是模板元编程中的关键部分之一:
template <typename T>
struct is_void
{
static const bool value = false;
}
template <>
struct is_void<void>
{
static const bool value = true;
}
Run Code Online (Sandbox Code Playgroud)
这本身就是你能做的很多事情的一个例子.模板本身是图灵完备的.
这忽略了非常基本的东西,例如范围,类型安全性,以及宏的问题.
cat*_*try 10
不.一个简单的反例:模板遵循命名空间,宏的忽略命名空间(因为它们是预处理器语句).
namespace foo {
template <class NumberType>
NumberType add(NumberType a, NumberType b)
{
return a+b;
}
#define ADD(x, y) ((x)+(y))
} // namespace foo
namespace logspace
{
// no problemo
template <class NumberType>
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
Run Code Online (Sandbox Code Playgroud)
C++模板有点像Lisp宏(不是C宏),因为它们在已经解析的代码版本上运行,它们允许您在编译时生成任意代码.不幸的是,你正在编写类似于原始Lambda演算的东西,所以像循环这样的高级技术有点麻烦.有关所有血腥细节,请参阅Krysztof Czarnecki和Ulrich Eisenecker的Generative Programming.
不,这不可能。预处理器对于像 T 的容器这样的一些事情来说(勉强)足够了,但是对于模板可以做的很多其他事情来说,它根本不够。
有关一些真实示例,请阅读Andre Alexandrescu 的Modern C++ Programming或Dave Abrahams 和 Aleksey Gurtovoy 的C++ Metaprogramming。这两本书中几乎没有任何东西可以用预处理器模拟到极小的程度。
编辑:就目前typename
而言,要求非常简单。编译器无法始终确定依赖名称是否引用类型。Usingtypename
显式地告诉编译器它引用了一个类型。
struct X {
int x;
};
struct Y {
typedef long x;
};
template <class T>
class Z {
T::x;
};
Z<X>; // T::x == the int variable named x
Z<Y>; // T::x == a typedef for the type 'long'
Run Code Online (Sandbox Code Playgroud)
typename
告诉编译器特定名称旨在引用类型,而不是变量/值,因此(例如)您可以定义该类型的其他变量。
归档时间: |
|
查看次数: |
19345 次 |
最近记录: |