我正在阅读一本有关模板工作原理的书,但我难以理解这段关于模板的解释。
它说:
当编译器看到模板的定义时,它不会生成代码。只有在我们实例化模板的特定实例时,它才会生成代码。代码仅在使用模板(而不是定义它)时生成,这影响了我们如何组织源代码以及错误何时被检测到......为了生成实例化,编译器需要具有定义函数模板或类模板成员函数的代码。因此,与非模板代码不同,模板头文件通常包括定义和声明。
“生成代码”到底意味着什么?我不明白编译函数模板或类模板与普通函数或类有何区别。
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 == compilation unit)中看到单个实现,以得到定义。非模板成员函数有什么特点使其能够在头文件之外被定义,而模板函数则没有?
请注意,您仍然可以针对特定类型专门化模板实现(包括在头文件中或特定的compilation unit中)。 如果您在其中一个compilation unit中为模板类提供了专门化,且未使用除专门化之外的其他类型,则也应足以将其全部链接在一起。
我希望这个示例有助于澄清编译器所做的区别和工作。
模板是创建代码的一种模式。当编译器看到一个模板的定义时,它会记录下该模式的相关信息。当它看到该模板的使用时,它会查找之前记录的信息,并在使用点推断应用该模式的方式,根据模式生成代码。
它不会立即生成代码。只有在遇到该模板的实例化时才生成类或模板代码。也就是说,如果您实际上正在创建该模板定义的对象。
本质上,模板使您可以摆脱类型。例如,如果您需要两个模板类的实例化,一个用于int类型,另一个用于double类型,编译器将在需要时为您创建这两个类。这就是使模板如此强大的原因。
def.hpp
#pragma once
template <typename T> // definition is not realy needed
T f(T a); // but why not for clarity
void f0();
void f1();
templates.cpp
#include "def.hpp"
template <typename T>
T f(T a) {
return a * a + a;
}
f0.cpp
#include "def.hpp"
#include "templates.cpp" // < -------
void f0() {
printf("f0 = %f\n", f<float>(7.0));
}
f1.cpp
#include "def.hpp"
#include "templates.cpp" // < -------
void f1() {
printf("f1 = %i\n", f<int>(-9));
}
main.cpp
#include "def.hpp"
#include "templates.cpp" // < -------
int main() {
f0();
f1();
printf("main = %f\n", f<double>(5.7));
}
不需要编译templates.cpp,因为它将被复制粘贴到其他.cpp文件中
g++ f0.cpp f1.cpp main.cpp -o main -lm
或者
g++ -c f0.cpp -lm
g++ -c f1.cpp -lm
g++ -c main.cpp -lm
g++ *.o -o main -lm
rm *.o
.cpp
(有一个不直接编译的.cpp
会让人感到困惑)。你应该将它包含在def.hpp
的底部,而不是手动添加。 - undefinedtemplates.cpp
,但没有将其传递给g++
。如果它不打算直接由GCC处理,那么它应该有一个不同的扩展名。 - undefined