我正在阅读一本关于模板如何工作的书,但我很难理解对模板的这种解释。
它说
当编译器看到模板的定义时,它不会生成代码。它仅在我们实例化模板的特定实例时生成代码。代码仅在我们使用模板时生成(而不是在定义模板时)这一事实会影响我们组织源代码的方式以及检测到错误的方式...要生成实例化,编译器需要具有定义函数模板或类模板成员函数。因此,与非模板代码不同,模板的标头通常包括定义和声明。
“生成代码”到底是什么意思?与常规函数或类相比,我不明白编译函数模板或类模板有什么不同。
编译器为模板类实例化中给出的特定类型生成代码。
例如,如果您有一个模板类声明
template<typename T>
class Foo
{
public:
T& bar()
{
return subject;
}
private:
T subject;
};
只要你有例如以下实例
Foo<int> fooInt;
Foo<double> fooDouble;
这些将有效地生成与您定义的类相同的可链接代码,例如
class FooInt
{
public:
int& bar()
{
return subject;
}
private:
int subject;
}
和
class FooDouble
{
public:
double& bar()
{
return subject;
}
private:
double subject;
}
并实例化变量,如
FooInt fooInt;
FooDouble fooDouble;
关于模板定义(无论模板如何都不要与声明混淆)需要与头(包含)文件一起看到的这一点,原因很清楚:
编译器无法在没有看到定义的情况下生成此代码。它可以指的是在链接阶段首先出现的匹配实例。
非模板成员函数有什么允许在模板函数没有的标头之外定义它?
非模板类/成员/函数的声明为链接器提供了预定义的入口点。该定义可以从已编译目标文件(== .cpp ==编译单元)中看到的单个实现中得出。
相反,模板化类/成员/函数的声明可以从给定相同或不同模板参数的任意编译单元实例化。这些模板参数的定义至少需要查看一次。它可以是通用的或专用的。
请注意,无论如何您都可以为特定类型专门化模板实现(包含在标头中或在特定的编译单元中)。如果您要在您的编译单元之一中为您的模板类提供专门化,并且不要将模板类与除专门化之外的类型一起使用,那么将它们链接在一起也应该足够了。
我希望这个示例有助于阐明编译器的不同之处和所做的努力。
模板是用于创建代码的模式。当编译器看到模板的定义时,它会记录该模式。当它看到使用该模板时,它会挖掘出它的注释,弄清楚如何在使用它的地方应用该模式,并根据该模式生成代码。
编译器在看到模板时应该做什么?为所有可能的数据类型生成所有机器代码 - 整数、双精度、浮点数、字符串……可能需要很多时间。或者只是有点懒惰并根据需要生成机器代码。
我想后一种选择是更好的解决方案并且可以完成工作。
这里的要点是编译器在遇到模板的某个实例之前不会处理模板定义。(然后它可以继续,我猜,就像它有一个普通的类,这是模板类的一个特定情况,具有固定的模板参数。)
您的问题的直接答案是:编译器从用户 c++ 代码生成机器代码,我认为这是 wat 在这里的意思是“生成代码”一词。
模板声明必须在头文件中,因为当编译器编译一些使用模板的源时,它只有头文件(包含在源中,带有#include 宏),但它需要整个模板定义。所以合乎逻辑的结论是模板定义必须在标题中。
当你创建一个函数并编译它时,编译器会为它生成代码。许多编译器不会为未使用的静态函数生成代码。
如果您创建了一个模板化函数并且没有使用该模板(例如 std::sort),则不会生成该函数的代码。
请记住,模板就像模具。模板告诉如何使用给定的模板参数生成类或函数。如果不使用模板,则不会生成任何内容。
还要考虑编译器不知道如何实现或使用模板,直到它看到所有模板参数都已解析。
它不会立即生成代码。仅在遇到该模板的实例时才生成类或模板代码。也就是说,如果您实际上是在创建该模板定义的对象。
本质上,模板允许您从类型中抽象出来。如果您需要模板类的两个实例化,例如一个 int 和一个 double,编译器会在您需要时为您创建其中两个类。这就是模板如此强大的原因。
您的 C++ 由编译器读取并转换为汇编代码,然后再转换为机器代码。
模板旨在允许通用编程。如果您的代码根本不使用您的模板,编译器将不会生成相关的汇编代码。将模板与程序中关联的数据类型越多,生成的汇编代码就越多。