C++ 模板只是伪装的宏吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/180320/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are C++ Templates just Macros in disguise?
提问by Roddy
I've been programming in C++ for a few years, and I've used STL quite a bit and have created my own template classes a few times to see how it's done.
我已经用 C++ 编程几年了,我已经使用了很多 STL 并且多次创建了我自己的模板类来看看它是如何完成的。
Now I'm trying to integrate templates deeper into my OO design, and a nagging thought keeps coming back to me: They're just a macros, really... You could implement (rather UGLY) auto_ptrs using #defines, if you really wanted to.
现在我正在尝试将模板更深入地集成到我的 OO 设计中,一个烦人的想法不断回到我身边:它们只是一个宏,真的......你可以使用#defines 实现(相当丑陋)auto_ptrs,如果你真的想要。
This way of thinking about templates helps me understand how my code will actually work, but I feel that I must be missing the point somehow. Macros are meant evil incarnate, yet "template metaprogramming" is all the rage.
这种对模板的思考方式有助于我理解我的代码实际上是如何工作的,但我觉得我一定是在某种程度上错过了这一点。宏意味着邪恶的化身,但“模板元编程”风靡一时。
So, what ARE the real distinctions? and how can templates avoid the dangers that #define leads you into, like
那么,真正的区别是什么?以及模板如何避免 #define 导致您陷入的危险,例如
- Inscrutable compiler errors in places where you don't expect them?
- Code bloat?
- Difficulty in tracing code?
- Setting Debugger Breakpoints?
- 在您不期望的地方出现难以理解的编译器错误?
- 代码膨胀?
- 代码追踪困难?
- 设置调试器断点?
回答by Ferruccio
Macros are a text substitution mechanism.
宏是一种文本替换机制。
Templates are a functional turing-complete language that is executed at compile time and is integrated into the C++ type system. You can think of them as a plugin mechanism for the language.
模板是一种功能图灵完备的语言,在编译时执行并集成到 C++ 类型系统中。您可以将它们视为该语言的插件机制。
回答by rlerallut
They are parsed by the compilerand not by a preprocessor that runs beforethe compiler.
它们由编译器解析,而不是由在编译器之前运行的预处理器解析。
Here's what MSDN says about it: http://msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx
以下是 MSDN 对此的说明:http: //msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx
Here are some problems with the macro:
- There is no way for the compiler to verify that the macro parameters are of compatible types.
- The macro is expanded without any special type checking.
- The i and j parameters are evaluated twice. For example, if either parameter has a postincremented variable, the increment is performed two times.
- Because macros are expanded by the preprocessor, compiler error messages will refer to the expanded macro, rather than the macro definition itself. Also, the macro will show up in expanded form during debugging.
以下是宏的一些问题:
- 编译器无法验证宏参数的类型是否兼容。
- 宏在没有任何特殊类型检查的情况下扩展。
- i 和 j 参数被评估两次。例如,如果任一参数具有后增量变量,则增量执行两次。
- 因为宏是由预处理器扩展的,所以编译器错误消息将引用扩展的宏,而不是宏定义本身。此外,宏将在调试期间以扩展形式显示。
If that's not enough for you, I don't know what is.
如果这对你来说还不够,我不知道什么是。
回答by Jeff B
There's a lot of comments here trying to differentiate macros and templates.
这里有很多评论试图区分宏和模板。
Yes - they are both the same thing: Code generation tools.
是的 - 它们都是一样的东西:代码生成工具。
Macros are a primitive form, without much compiler enforcement (like doing Objects in C - it can be done, but it's not pretty). Templates are more advanced, and have a lot better compiler type-checking, error messages, etc.
宏是一种原始形式,没有太多的编译器强制执行(就像在 C 中执行对象一样 - 它可以完成,但并不漂亮)。模板更高级,并且有更好的编译器类型检查、错误消息等。
However, each has strengths that the other does not.
然而,每个人都有另一个没有的优势。
Templates can only generate dynamic class types - macros can generate almost any code you want (other than another macro definition). Macros can be very useful to embed static tables of structured data into your code.
模板只能生成动态类类型 - 宏几乎可以生成您想要的任何代码(除了另一个宏定义)。宏对于将结构化数据的静态表嵌入到代码中非常有用。
Templates on the other hand can accomplish some truly FUNKY things that are not possible with macros. For example:
另一方面,模板可以完成一些宏无法实现的真正 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!");
}
The template allows the compiler to dynamically create and use type-safe instances of the template on-the-fly. The compiler actually does the template-parameter math at compile time, creating separate classes where needed for each unique result. There is an implied Unit<1,-1> (distance / time = velocity) type that is created and compared within the conditional, but never explicitly declared in code.
模板允许编译器动态创建和使用模板的类型安全实例。编译器实际上在编译时进行模板参数数学运算,为每个唯一结果在需要的地方创建单独的类。有一个隐含的 Unit<1,-1>(距离/时间 = 速度)类型,它在条件中创建和比较,但从未在代码中明确声明。
Apparently, someone at a university has defined a template of this sort with 40+ parameters (need a reference), each representing a different physics unit type. Think about the type-safety of that sort of class, just for your numbers.
显然,某大学的某个人定义了一个此类模板,其中包含 40 多个参数(需要参考),每个参数代表不同的物理单元类型。想想那种类的类型安全性,只是为了你的数字。
回答by Gregory Pakosz
The answer is so long I can't sum up everything but:
答案太长了,我无法总结所有内容,但是:
- for instance macros don't ensure type safety while function templates do: there is no way for the compiler to verify that the macro parameters are of compatible types -- also at the time the function template is instantiated the compiler knows whether
int
orfloat
defineoperator +
- templates open the door for metaprogramming (in short, evaluating things and taking decision at compile time): at compile time it's possible to know whether a type is integral or floating point; whether it's a pointer or whether it's const qualified, etc... see "type traits" in upcoming c++0x
- class templates have partial specialization
- function templates have explicit full specialization, in your example
add<float>(5, 3);
could be implemented differently thanadd<int>(5, 3);
which isn't possible with macros - macro don't have any scope
#define min(i, j) (((i) < (j)) ? (i) : (j))
- thei
andj
parameters are evaluated twice. For example, if either parameter has a postincremented variable, the increment is performed two times- because macros are expanded by the preprocessor, compiler error messages will refer to the expanded macro, rather than the macro definition itself. Also, the macro will show up in expanded form during debugging
- etc...
- 例如,宏不能确保类型安全,而函数模板可以:编译器无法验证宏参数是否具有兼容类型——同样在函数模板被实例化时,编译器知道是否
int
或float
定义operator +
- 模板为元编程打开了大门(简而言之,在编译时评估事物并做出决定):在编译时可以知道类型是整型还是浮点型;无论是指针还是常量限定等等……请参阅即将发布的 c++0x 中的“类型特征”
- 类模板具有部分特化
- 函数模板具有明确的完全专业化,在您的示例中
add<float>(5, 3);
可以实现add<int>(5, 3);
与宏无法实现的不同 - 宏没有任何作用域
#define min(i, j) (((i) < (j)) ? (i) : (j))
-i
和j
参数被评估两次。例如,如果任一参数具有后增量变量,则增量执行两次- 因为宏是由预处理器扩展的,所以编译器错误消息将引用扩展的宏,而不是宏定义本身。此外,宏将在调试期间以扩展形式显示
- 等等...
Note: In some rare cases, I preferred relying on variadic macros because there is no such thing as variadic templates until c++0x becomes mainstream.C++11is live.
注意:在一些罕见的情况下,我更喜欢依赖可变参数宏,因为在 c++0x 成为主流之前,没有可变参数模板这样的东西。C++11 上线了。
References:
参考:
回答by GManNickG
On a very basic level, yes, template's are just macro replacements. But you're skipping out on a lotof things by thinking about it that way.
在非常基本的层面上,是的,模板只是宏替换。但是,通过这种方式思考,您会跳过很多事情。
Consider template specialization, which to my knowledge you can't simulate with macro's. Not only does that allow, well, special implementation for certain types, it's one of the key parts in template meta-programming:
考虑模板专业化,据我所知,你不能用宏来模拟。这不仅允许特定类型的特殊实现,它还是模板元编程中的关键部分之一:
template <typename T>
struct is_void
{
static const bool value = false;
}
template <>
struct is_void<void>
{
static const bool value = true;
}
Which in itself is just one example of the many things you can do. Templates themselves are Turing-complete.
这本身只是您可以做的许多事情中的一个例子。模板本身是图灵完备的。
This ignores the very basic things, such as scope, type-safety, and that macro's are messier.
这忽略了非常基本的东西,例如作用域、类型安全,而且那个宏更混乱。
回答by catchmeifyoutry
NO. One simple counter example: templates abide to namespaces, macro's ignore namespaces (as they are preprocessor statements).
否。一个简单的反例:模板遵守命名空间,宏忽略命名空间(因为它们是预处理器语句)。
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
回答by Andru Luvisi
C++ templates are kind of like Lisp macros (not C macros) in that they operate on the already parsed version of the code and they let you generate arbitrary code at compile time. Unfortunately, you are programming in something resembling the raw Lambda calculus, so advanced techniques like looping are kind of cumbersome. For all of the gory details, see Generative Programmingby Krysztof Czarnecki and Ulrich Eisenecker.
C++ 模板有点像 Lisp 宏(不是 C 宏),因为它们在已经解析的代码版本上运行,并且它们允许您在编译时生成任意代码。不幸的是,您正在使用类似于原始 Lambda 演算的东西进行编程,因此循环等高级技术有点麻烦。有关所有血腥细节,请参阅Krysztof Czarnecki 和 Ulrich Eisenecker 的生成式编程。
回答by Ryan
In case you are looking for a more in-depth treatment of the subject, I can turn you to everyone's favorite C++ hater. This man knows and hates more C++ than I can ever dream to. This simultaneously makes the FQA incredibly inflammatory and an excellent resource.
如果您正在寻找对该主题的更深入处理,我可以将您转为每个人最喜欢的 C++ 仇恨者。这个人知道和讨厌 C++ 比我做梦到的都要多。这同时使 FQA 具有令人难以置信的煽动性和极好的资源。
回答by moonshadow
- templates are typesafe.
- templated objects / types can be namespaced, made private members of a class etc.
- parameters to templated functions are not replicated throughout the function body.
- 模板是类型安全的。
- 模板化对象/类型可以命名空间,成为类的私有成员等。
- 模板化函数的参数不会在整个函数体中复制。
These really are a big deal and prevent a multitude of bugs.
这些确实很重要,可以防止大量错误。
回答by Jerry Coffin
No, it's not possible. The preprocessor is (barely) sufficient for a few things like containers of T, but it's simply insufficient for quite a few other things templates can do.
不,这不可能。预处理器(勉强)足以处理 T 的容器之类的一些事情,但对于模板可以做的很多其他事情来说,它根本不够用。
For some real examples, read through Modern C++ Programming, by Andre Alexandrescu, or C++ Metaprogrammingby Dave Abrahams and Aleksey Gurtovoy. Nearly nothing done in either book can be simulated to any more than an extremely minimal degree with the preprocessor.
有关一些真实示例,请阅读Andre Alexandrescu 的Modern C++ Programming或Dave Abrahams 和 Aleksey Gurtovoy 的C++ Metaprogramming。这两本书中几乎没有任何东西可以用预处理器模拟到极小的程度。
Edit: As far as typename
goes, the requirement is pretty simple. The compiler can't always figure out whether a dependent name refers to a type or not. Using typename
explicitly tells the compiler that it refers to a type.
编辑:就目前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'
typename
tells the compiler that a particular name is intended to refer to a type, not a variable/value, so (for example) you can define other variables of that type.
typename
告诉编译器特定名称旨在引用类型,而不是变量/值,因此(例如)您可以定义该类型的其他变量。