C++模板编程(4)---实际运用模板:置入式模型(InclusionModel)

有很多方法可以组织我们的模板template程序代码,本文介绍常见的一种方式:置入式模型。

1. 链接错误(Link Error)

大多数C,c++程序员大致按以下方式来组织他们的non-template程序代码:

Classes和其他类型被全体放置于头文件(header files)。通常头文件的后缀名称(扩展名)为.hpp(或.H, .h,.hh,.hxx 等等)。

全局变量和non-inline函数只在头文件中置入声明语句,定义式则置于.c文件中。这里的.c文件是个统称,通常其后缀名称(扩展名)为.cpp(或.c,.C,.cc,.cxx等等)。

这种方式运作良好,程序能够轻易找到各个类型的定义,并避免一个变量或函数被多次重复定义,于是连接器(linker)可以正常工作。

但是模板程序代码在这里跟非模板程序不一样,举例来说,我们把 template声明于某个头文件:

//myfirst.hpp

#ifndef MYFIRST_HPP

#define MYFIRST_HPP

//template 声明语句

template <typename T>

void print_typeof (T const&);

#endif

print_typeof()是一个简单的辅助函数,它打印参数的类型信息(type information)。函数的实际代码被置于一个.c文件中:

//myfirst.cpp

#include <iostream>

#include <typeinfo>

#include "myfirst.hpp"

//template定义式

template <typename T>

void print_typeof (T const& x)

{

        std::cout << typeid(x).name() << std::endl;

}

 这个例子中使用typeid运算符,把参数的类型以字符串形式打印出来。

此后,我们又在另外一个c文件中使用这个template,并将template声明语句(所在的文件)以

#include包含进来:

//main.cpp

#include "myfirst.hpp"

int main()

{

        double ice=3.0;

        print_typeof(ice);

}

大多数编译器可以正确编译上述代码,但会报告一个链接错误,表示无法找到print_typeof()函数的定义。

    错误的原因在于,function template print-typeof()的定义没有被实例化。为了实例化一个模板,编译器必须知道以哪一份定义式以及哪些模板参数对它实例化。不幸的是先前这个例子中,这两项信息被分置在两个分开编译的文件中。因此,当编译器看到对print_typeof()的调用时,看不到其定义,无法以double类型来实例化print_typeof()。于是编译器假设这个template的定义位于其他某处,因而只生成一个对该定义的reference,并将这个reference所指的定义式留给连接器去解决(resolve)。另一方面,当编译器处理myfirst.cpp时,它又察觉不到该以哪个自变量类型来实例化其定义式。

2. 把模板放在头文件中(Header Files)

    就像处理宏macro和内联函数inline一样,解决上面问题的常见办法是:把template定义式放到其声明语句所在的头文件中。面对前例,我们可以在myfirst.hpp最后一行加入:

#include “myfirst.cpp”

也可以在用到该template的每一个.c文件中#include “myfirst.cpp”.

第三种方法,也是最常用的方法,完全不用myfirst.cpp,声明和定义都写在文件myfirst.hpp中:

//myfirst.hpp

#ifndef MYFIRST_HPP

#define MYFIRST_HPP

#include <iostream>

#include <typeinfo>

// 声明语句

template <typename T>

void print_typeof (T const &)

//定义语句

template <typename T>

void print_typeof(T const& x)

{

        std::cout << typeid(x).name() << std::endl;

}

#endif

这种template组织法称为置入式模型inclusion model。现在上面程序可以正确编译、链接与运行。

需要引起注意的是:

包含myfirst.hpp的代价比较大。此物所谓代价,不仅指template定义式的体积增大,也包括含入myfirst.hpp中其他的文件,比如<iostream>和<typeinfo>。我们会发现,可能导致成千上万的代码被包含进来,因为诸如<iostream>也是以此种方式来定义template的。

尽管存在上面的问题,还是强烈推荐:只要有可能,尽量用置入式模型inclusion model来组织模板代码。

另外一个关于置入式模型的考虑是,与内联函数(inline function)和宏(marco)明显不同的是,非内联函数模板function template并不在调用端被展开。每当它们被实例化一次,编译器便从头创建一份函数拷贝。这个过程是完全自动化的,编译器可能最终在不同的文件中创建同一个模板实例化的两份拷贝,这时连接器会报错。但理论上,不需要程序员操心,这应该是编译系统的事。

最后要指出的是,

   本文中对于function template所谈的内容,也适用于类模板class template的成员函数和static 成员变量。当然也同样适用于成员函数模板member function templates。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值