今天刷牛客网面经的时候,看到一个阿里计算平台部门一面的一个面试题,感觉有点意思,是这么问的:
为什么模板的定义跟声明要在同一个文件中?
如果对模板不太熟悉的话,或者模板用的不多可能就会当场懵逼,比如我这种的,要是问到肯定就Over了,于是我默默的拿起C++ Primer好好看了看模板那一章(虽然之前看过,但是模板用的不多,很轻易的就忘记了),发现其实这个问题书中有拿出单独的一小节在讲,16.3模板编译模型(我的书是第四版的)。
正常来说,写C++代码的时候,按照惯例,类声明放在.h文件中,类定义放在.cpp文件中,相应的成员函数声明也在.h文件中,定义则在.cpp文件中,但是奇怪的是如果这样的架构用在模板函数中,在调用函数模板的地方链接器会报错:
error: ld returned 1 exit status如果将定义和声明写在相同的文件中问题就解决了。简单的说就是因为大部分编译器在编译模板的时候都使用的是包含编译模型,在实例化模板的时候,编译器必须能够访问定义模板的源代码,而编译器一次只能处理一个编译单元,也就是一次只能处理一个cpp文件。这不同于普通的函数,在使用普通的函数时,编译时只需要看到该函数的声明即可编译,而在链接时由链接器来确定函数的实体。
但是也不是说非得要在一个文件中,我们可以在头文件中添加一条#include指示使定义可用,通过#include 来引入包含相关定义的源文件。如下图:
代码如下:
//header file utlities.h #ifndef UTLITIES_H #define UTLITIES_H template<class T> int compare(const T&, const T&); //other declarations #include "utlities.cpp" // get the definitions for compare etc. #endif //implementation file utlities.cpp template<class T> int compare(const T &v1, const T &v2) { if(v1 < v2) return -1; else if (v1 > v2) return 1; else return 0; } //other definitions //main.cpp #include <iostream> #include "utlities.h" using namespace std; int main(){ cout << compare(1,0) <<endl; }除了上面说的包含编译模型,C++还提供了模板的分别编译模型(separate compilation model),编译器会为我们跟踪相关的模板定义。但是,我们必须让编译器知道记住给模板定义,可以使用export关键字来做这件事。export关键字能够指明给定的定义可能会需要在其他文件中产生实例化。需要注意的是所有编译器都支持第一种编译模型,只有一些编译器支持第二种模型,所以不太建议使用第二种方案。具体使用方法可以参考C++ Primer P646.
<完>
2017-09-29 20:04