我有两个关于 C++ 模板的问题。假设我编写了一个简单的 List,现在我想在我的程序中使用它来存储指向不同对象类型(A*、B* ... ALot*)的指针。我的同事说,对于每种类型,都会生成一段专用的代码,即使所有指针实际上都具有相同的大小。
是的,这相当于编写了两个函数。
一些链接器会检测到相同的功能,并消除它们。一些库意识到他们的链接器没有此功能,并将通用代码分解为单个实现,只留下围绕通用代码的强制转换包装器。即,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 生成专用代码(考虑到它们具有相同的大小并且没有专门化)。
它们是不同的类型。我可以编写与 a char
orshort
值不同的代码。举个例子:
std::cout << x << "\n";
x 为 ashort
时,打印一个整数,其值为x
-x
为 achar
时,打印对应于 的字符x
。
现在,几乎所有的模板代码都存在于头文件中,并且是隐含的inline
. 虽然inline
并不意味着大多数人认为的意思,但它确实意味着编译器可以轻松地将代码提升到调用上下文中。
如果这有什么不同,我们谈论的是嵌入式应用程序。
真正重要的是您的特定编译器和链接器是什么,以及它们具有哪些设置和标志。