C ++模板只是一种美化的宏吗?


27

通过C ++模板和C#/ Java泛型之间的不同比较,例如:

/programming/31693/what-are-the-differences-between-generics-in-c-and-java-and-templates-in-c/31929#31929

我感觉到,C ++模板是通过某种预处理(在解析之前替换纯文本)而不是通过编译实现的。因为C ++模板中的类型检查类似于C宏。我的意思是,如果存在一些错误,它们是处理模板代码块后生成的代码中的错误,而不是模板本身中的错误。换句话说,它们只是C语言中宏的一种上层版本。

然后我发现了其他一些事实支持这一点-

  • 我认为,如果C ++模板通过预处理实现,则动态链接(使用.dll)会出现问题。快速搜索支持了这一点。

  • 另一点是,整数常量可以作为参数传递给模板。而且它甚至支持某种递归。但是在编译的汇编/机器代码中找不到此递归。通过为每个递归调用生成函数,可以在编译时管理递归对象,因此具有更大但更快的可执行二进制文件。

尽管与C宏不同,但它具有一些优越的功能。但是C ++模板不是通过某种预处理实现的吗?如何在不同的C ++编译器中实现?


19
不。C ++模板已编译。
爱德华·斯特兰奇

2
您对“预处理”的定义是什么?和“编译”?足够广泛的“预处理”定义可以包含编译器所做的一切;毕竟,编译器实际上只是在执行源之前对其进行处理,不是吗?
James McNellis 2011年

@James McNellis恕我直言,如果您可以将预处理与其他所有编译工作区分开,那么足以理解我的问题。澄清预处理程序-en.wikipedia.org/wiki/Preprocessor#Lexical_preprocessors
Gulshan

6
如果您指的是这种预处理形式,那么不,C ++模板绝对不只是某种美化的宏。
James McNellis 2011年

1
模板语言实际上是完整的,因此它们不仅仅是增强的宏。
davidk01 2011年

Answers:


9

C ++模板是一种笨拙的Lisp(甚至更多,Scheme)宏。它是一种图灵完备的语言,会在编译时进行评估,但由于无法从该语言访问底层C ++环境,因此受到了严重限制。因此,是的,C ++模板可以看作是某种形式的预处理,与所生成的代码的交互作用非常有限。


2
“但是它受到严格限制,因为该语言无法访问底层的C ++环境。” - 这是什么意思?我试图解析此语句,但失败了。
quant_dev

2
嗯,实际上... github.com/kmichel/bf0x
Anton

3
@ SK-logic:顺便说一句,C ++ 11 C ++,除非您(在脚步上)将同一语言的不同版本视为不同的语言。
乔恩·普迪

3
@Jon Purdy,在回答此问题时(正式)还不存在C ++ 11。时下一个例子将被更复杂的,像分解的数据结构,使用库功能等
SK-逻辑

1
@ SK-logic:您是否偶然知道使用类似Lisp的宏实现C ++?我已经厌倦了模板限制。Haxe中具有强大的宏系统的C ++样式语法语言示例:haxe.org/manual/macros。(因为我使用C ++达到目的-对8位微控制器进行编程,这无济于事;还有其他更好的语言)。
pfalcon

41

可能最大的不同是C宏在预处理阶段被扩展,而不进行其他任何编译,而C ++模板是编译的一部分。这意味着C ++模板具有类型感知和作用域,并且不是简单的文本替换。它们可以编译为实际函数,因此可以避免宏存在的大多数问题。意识到类型意味着它们可以是通用的也可以是专用的:例如,提供swap模板功能很容易,并且即使对象管理堆内存,编写易于工作的专用化也很容易。

因此:C ++模板不是在相同意义上进行预处理的宏,它们不是C宏的一种,并且不可能使用C宏来复制模板所做的事情。

模板存放在头文件中,而不是在链接库中,是的,但是,如果您提供的是.dll,则可能还提供了要使用的头文件。


12
它们不需要驻留在头文件中(这只是使用它们的最简单方法)。您可以在源文件中定义它们,并在源文件(编译单元)中手动强制实例化模板。然后,链接将照常拾取实例化(这是一种将模板限制为特定类型而不允许所有通用类型的技术)。
马丁·约克

@Martin:我不确定标准是否明确支持此技术(虽然受支持)。否则,所有编译器将已经export实现。我知道这种方法对函数有效,但是我怀疑它对类有效:您怎么知道它们的大小?
Matthieu M.

@Matthieu M:这与export关键字无关。它处理“ Explicit”模板实例化,并且在标准中定义良好。
马丁·约克

2
@Matthieu M .:如果编译器知道函数的签名,并且链接器可以找到实现,那么一切都很好。无论该功能是否为模板功能,均适用。在实践中,它们通常位于头文件中,因为强制执行特定的实例通常比其应有的工作还要多,但是Martin指出替代方法是正确的。
David Thornley

我知道它确实可以用于特殊功能。但是我也很确定,它不适用于类,这就是我的观点。我认为它也适用于特殊类的方法,但是不知道这是否是标准的。
Matthieu M.

5

它们的实施方式有关系吗?早期的C ++编译器只是将代码提供给ac编译器的预处理器,并不意味着C ++只是美化的宏。

模板通过提供一种更安全,更高效,更专业的方式(甚至我不认为这是一个真正的词)来实现多种类型的代码,从而消除了对宏的需求。

有多种方法可以在c中对类型代码进行模板化,一旦超越了简单类型,这些方法都不是很好。


我还没有说过C ++是美化的宏。我非常欣赏C ++。只是好奇。
Gulshan

2
@Gulshan:不,你什么都没说。但是,这就是早期C ++编译器的工作方式(除了CFront是某种编译器,而不仅仅是宏预处理器),并且您有关C ++模板的声明也适用于早期C ++本身。
David Thornley'3

CFront将C ++ 编译为C。预处理器和编译器之间的界线明确定义:编译器尝试通过解析,AST构建等方式理解其输入,而预处理器则不理解(例如,仅使用文本或标记替换)。
乔恩·普迪

1
我认为David的意思是存在C ++的底层子集,并且可以将模板视为某种宏,用于在该子集中通用程序,然后可以将其编译为另一个单独的阶段。
乔治

5

有一些区别;例如,可以在需要时使用模板来实例化函数重载,而对于宏,则必须为每个可能的重载扩展一次宏,以使其对编译器可见,因此最终会产生很多结果未使用的代码。

另一个区别是模板使用命名空间。


2

恕我直言,C ++模板和C宏旨在解决两个完全不同的问题。原始的C ++标准模板库是一种将容器类(数组,链接列表等)与通常应用于它们的通用函数(例如排序和串联)干净地分离的机制。对有效算法和数据结构进行抽象表示可以提高代码的表达力,因为在如何最好地实现对特定数据片段的功能方面,猜测的工作量大大减少了。C宏与Lisp宏中通常看到的内容更加一致,因为它们提供了一种用内联代码“扩展”该语言的方法。很棒的事情是,C ++标准库扩展了模板的功能,以涵盖我们在C语言中使用#define的绝大多数功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.