模板如何分离式编译
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
先讲一下事情的起因。许多天以前我写了一个cpp文件其中定义了一个模板cpp文件中还有一个main函数调用了这个模板的一个实例。
昨天我在一个新的cpp文件中想调用那个模板于是我就想到了联合编译这两个cpp文件。一直报错倒腾了许久今天弄明白后记录一下这个过程和一些注意点。
首先不能有两个 main
函数
联合编译先是报了一个 multiple definition of `main’ 的错然后又报了一个undefined reference的错。
先解决第一个错误。这说来就闹笑话了。两个cpp文件中都有 main
函数。要联合编译肯定不能出现两个 main
函数啊不然到底运行哪个文件 main
函数
因为我们想要调用第一个cpp文件的模板所以把这个文件里面的 main
函数删掉就行了。这是一个愚蠢的错误。
实例化的条件是有定义且有调用
现在两个cpp文件是这样的做了简化
// Add.cpp
template <typename T>
T Add(T x1, T x2)
{
return x1 + x2;
}
// AddMain.cpp
#include <iostream>
template <typename T> T Add(T, T);
int main()
{
std::cout << Add(1, 2) << '\n';
return 0;
}
其实至此我们已经可以在 AddMain.cpp
中通过 #include "Add.cpp"
来直接调用 Add
了第10行模板声明都可以去掉然后直接编译运行 AddMain.cpp
多省事。但我们就不就要分离式编译
命令行联合编译
g++ Add.cpp AddMain.cpp
报错
undefined reference to `int Add<int>(int, int)’
这说明我们要调用的函数尚未定义。为什么呢原因是这是一个函数模板而不是一个函数函数模板只有在实例化后才是一个函数。言下之意就是没有触发模板实例化。
默认情况下模板实例化要满足两个条件既要有定义又要有调用。事实上我们会发现单独编译两个文件没有任何问题。
g++ -c Add.cpp # 生成Add.o
g++ -c AddMain.cpp # 生成AddMain.o
g++ Add.o AddMain.o # 报错undefined reference to `int Add<int>(int, int)'
问题出在链接一步。编译 Add.cpp
时只有定义没有调用因此不会实例化Add.o
里面就不会有实例编译 AddMain.cpp
时只有调用未见定义因此也不会实例化此时编译器会生成 call Add
的指令以期在链接时在其他扩展名为 o
的文件中寻找 Add
函数故 AddMain.o
也没有实例。链接的时候 Add.o
和 AddMain.o
就大眼瞪小眼啦
AddMain.oAdd.o老哥请给我一个
Add\<int\>
实例Add.o我这里没发生调用啊哪来实例呢你没有实例吗
AddMain.o我当然没有啦我指望你呢
Add.o啊这,ԾㅂԾ,
必须显式实例化才能进行分离式编译
于是我们必须在某个地方要求模板显式实例化。最简单的做法当然是在 Add.cpp
里面添加一个实例化定义。但这样做多少有点不太好尤其是在大型项目中改模板源文件就意味着所有调用模板的地方要重新编译。所以我们不想对模板源文件做任何改动。
那我们就新建一个文件来进行实例化定义。实例化定义的语法是 template 函数声明/类声明;
。
// AddBuild.cpp
#include "Add.cpp"
template int Add(int x1, int x2);
联合编译 AddBuild.cpp
和 AddMain.cpp
并指定可执行文件名为 AddMain
命令行运行
g++ AddBuild.cpp AddMain.cpp -o AddMain # 无报错
.\AddMain # 运行AddMain.exe
输出结果为3。
用 Add.cpp
还是 Add.h
问题到这里也就解决了。还有句题外话前面代码里面有一个 #include "Add.cpp"
。啥cpp
文件也可以被 include
是的虽然一般都是 include
头文件但C++是一门包容的语言include
一个 cpp
文件也是无可厚非的毕竟 include
做的工作就是把别处代码拷贝过来而已。不过为了符合一般的习惯我们当然可以改成头文件的形式
// Add.h
#ifndef ADD_H
#define ADD_H
template <typename T>
T Add(T x1, T x2)
{
return x1 + x2;
}
#endif