【转】类模版的分离编译问题

转自http://blog.csdn.net/woshishuizzz/article/details/8648440前端

 模板不是数据类型,只能算是一种行为集合的表示。编译器在使用模板时,经过更换模板参数来建立数据类型。这个过程就是模板实例化(Instantiation), 从模板类建立获得的类型称之为特例(specialization),说白了就是建立了一个新类型。 模板实例化取决于编译器可以找到可用代码来建立特例(称之为实例化要素,point of instantiation),也就是说,编译器不但要看到模板的声明,还要看到模板的定义。面试

1. 为何类模版不能分离编译,这要从模板的实例化过程提及:函数

    1)以分离形式写出的模版类(以tem.h和tem.cpp为例,另外还有主函数main.cpp),在编译main.cpp时因为只能看到模板声明而看不到实现,所以不会建立新的类型,但此时不会报错,由于编译器认为模板定义在其它文件中,就把问题留给连接程序处理。ui

    2)编译器在编译tem.cpp时能够解析模板定义并检查语法,但不能生成成员函数的代码。由于要生成代码,须要知道模板参数,即须要一个类型,而不是模板自己。.net

    3)这样,连接程序在main.cpp 或 tem.cpp中都找不到新类型的定义,因而报出无定义成员的错误。blog

另外,实例化是惰性的,只有用到该函数时才会去对模版中的定义进行实例化。ci

2. 如何解决这个问题?有三种方式:get

    1)在实例化要素中让编译器看到模板定义。(即写到一个文件里)编译器

    2)用一个新的文件来显式地实例化类型,这样连接器就能看到该类型。it

    3)使用export关键字。

    前二种方法一般称为包含模式,第三种方法则称为分离模式。第一种方法意味着在使用模板的转换文件中不但要包含模板声明文件,还要包含模板定义文件,这样编译器就能看到模板的声明和定义。这样作的缺点是编译文件会变得很大,显然要下降编译和连接速度。第二种方法,经过显式的模板实例化获得类型,也就是将全部的显式实例化过程安放在另外的文件中(好比tem_extend.cpp)。这样新类型不是在main.cpp中产生,而是在tem_extend.cpp中产生,连接器就可以找到它的定义。用这种方法,不会产生巨大的头文件,加快编译速度,并且头文件自己也显得更加“干净”和更具备可读性。但这个方法不能获得惰性实例化的好处,即它将显式地生成全部的成员函数。第三种方法是在模板定义中使用export关键字,剩下的事就让编译器去自行处理了。

3. 其余

    vc和gcc都只支持h文件里反过来include cpp文件的那种所谓的“模板分离编译”,虽然把h和cpp分开了,但编译的时候仍是合成了一个h文件,真正的分离编译是export关键字,而export关键字,任何主流编译器不支持而且之后都不打算支持。“靠谱的编译器都不支持模板分离编译,支持模板分离编译的编译器都不靠谱”。

    另外,EDG是第一个支持C++ export关键字的前端编译器,可是它不推荐使用这个关键字,之后将再也不支持export。

    参考:1)模板分离编译过程,and 2)昨天面试的几道小题

4. 另外的另外

    不少C/C++编译器对函数参数的压栈都是自右向左的,即最右的参数被首先push到栈底,而后load到寄存器中进行下一步运算。这在一些多个参数均带有同一变量的自增或自减的函数中会出现难以想象的结果。

相关文章
相关标签/搜索