编译器将C++为每个模板类型生成代码

Will C++ compiler generate code for each template type?

本文关键字:类型 代码 C++ 编译器      更新时间:2023-10-16

我有两个关于C++模板的问题。假设我已经编写了一个简单的列表,现在我想在我的程序中使用它来存储指向不同对象类型(A*,B* ...分配*(。我的同事说,对于每种类型,都会生成一段专用的代码,即使所有指针实际上都具有相同的大小。

如果这是真的,有人能解释我为什么吗?例如,在 Java 中,泛型与 C++ 中指针的模板具有相同的用途。泛型仅用于预编译类型检查,并在编译前被剥离。当然,所有内容都使用相同的字节码。

第二个问题是,是否也会为 char 和 short 生成专用代码(考虑到它们的大小相同并且没有专用化(。

如果这有什么不同,我们谈论的是嵌入式应用程序。

发现了一个类似的问题,但它并没有完全回答我的问题:C++模板类是否为使用的每种指针类型复制代码?

多谢!

我有两个关于C++模板的问题。假设我已经编写了一个简单的列表,现在我想在我的程序中使用它来存储指向不同对象类型(A*,B* ...分配*(。我的同事说,对于每种类型,都会生成一段专用的代码,即使所有指针实际上都具有相同的大小。

是的,这相当于编写了两个函数。

一些链接器会检测相同的函数,并消除它们。 一些库意识到其链接器没有此功能,并将公共代码分解到单个实现中,只在公共代码周围留下一个强制转换包装器。 也就是说,std::vector<T*>专业化可能会将所有工作转发给std::vector<void*>然后在出路时进行铸造。

现在,comdat 折叠很微妙:制作您认为相同的函数相对容易,但最终却不一样,因此生成了两个函数。 举个玩具例子,你可以通过typeid(x).name()打印字体名。 现在函数的每个版本都是不同的,无法消除它们。

在某些情况下,你可能会做这样的事情,认为它是一个不同的运行时属性,因此将创建相同的代码,并消除相同的函数 - 但聪明的C++编译器可能会弄清楚你做了什么,使用as-if规则并将其转换为编译时检查,并阻止不真正相同的函数被视为相同。

如果这是真的,有人能解释我为什么吗?例如,在 Java 中,泛型与 C++ 中指针的模板具有相同的用途。泛型仅用于每个编译的类型检查,并在编译之前被剥离。当然,所有内容都使用相同的字节码。

不,他们不是。 泛型大致等同于类型擦除的C++技术,例如std::function<void()>存储任何可调用对象的作用。 在C++中,键入擦除通常通过模板完成,但并非所有模板的使用都是类型擦除!

C++对本质上不是类型擦除的模板执行的操作通常不可能使用 Java 泛型执行。

在C++中,您可以使用模板创建一个指针的类型擦除容器,但std::vector不会这样做 - 它会创建一个实际的指针容器。 这样做的好处是,std::vector上的所有类型检查都是在编译时完成的,因此不必进行任何运行时检查:安全类型擦除std::vector可能需要运行时类型检查和所涉及的相关开销。

第二个问题是,是否也会为 char 和 short 生成专用代码(考虑到它们的大小相同并且没有专用化(。

它们是不同的类型。 我可以编写代码,这些代码在charshort值下的行为会有所不同。 举个例子:

std::cout << x << "n";

x是short,这打印一个整数,其值为x -- x是一个char,这打印了对应于x的字符。

现在,几乎所有的模板代码都存在于头文件中,并且隐式inline。 虽然inline并不意味着大多数人认为它的意思,但它确实意味着编译器可以轻松地将代码提升到调用上下文中。

如果这有什么不同,我们谈论的是嵌入式应用程序。

真正与众不同的是您的特定编译器和链接器是什么,以及它们具有哪些设置和标志处于活动状态。

答案是也许。 通常,每个实例化模板是唯一的类型,具有唯一的实现,并且将生成一个完全独立的代码实例。合并实例是可能的,但会考虑"优化"(在"好像"规则下(,以及此优化传播范围不广。

关于与Java的比较,有几点要记住:

  • 默认情况下,C++使用值语义。 一个std::vector , 对于例如,将实际插入副本。 无论你是复制shortdouble确实会对生成的代码。 在 Java 中,shortdouble 将被装箱,生成的代码将以某种方式克隆盒装实例;克隆不需要不同的代码,因为它调用虚拟Object的功能,但物理复制可以。

  • C++比Java强大得多。 特别是,它允许比较诸如函数地址之类的东西,它需要模板的不同实例中的函数具有不同的地址。 通常,这不是重点,我可以很容易地想象一个编译器,有一个选项告诉它忽略了这一点,并合并了在二进制级别相同。 (我认为VC++有类似的东西这。

另一个问题是模板的实现C++必须存在于头文件中。 当然,在Java中,一切都必须始终存在,因此此问题会影响所有人类,而不仅仅是模板。 当然,这是其中之一Java 不适合大型应用程序的原因。 但这意味着您不希望在模板;这样做失去了C++的主要优势之一,与Java(和许多其他语言(相比。 其实不然很少见,在模板中实现复杂的功能时,使模板继承自非模板类在void*方面进行了大部分实现。 而从void*方面实现大型代码块从来都不是有趣,它确实具有提供两者的优点客户端的世界:实现隐藏在编译中文件,以任何方式、形状或方式对客户端不可见。