当前位置:嗨网首页>书籍在线阅读

19-实例化和具体化

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.5.4 实例化和具体化

为进一步了解模板,必须理解术语实例化和具体化。记住,在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)。例如,在程序清单8.13中,函数调用Swap(i, j)导致编译器生成Swap()的一个实例,该实例使用int类型。模板并非函数定义,但使用int的模板实例是函数定义。这种实例化方式被称为隐式实例化(implicit instantiation),因为编译器之所以知道需要进行定义,是由于程序调用Swap()函数时提供了int参数。

最初,编译器只能通过隐式实例化,来使用模板生成函数定义,但现在C++还允许显式实例化(explicit instantiation)。这意味着可以直接命令编译器创建特定的实例,如Swap()。其语法是,声明所需的种类——用<>符号指示类型,并在声明前加上关键字template:

template void Swap<int>(int, int); // explicit instantiation

实现了这种特性的编译器看到上述声明后,将使用Swap()模板生成一个使用int类型的实例。也就是说,该声明的意思是“使用Swap()模板生成int类型的函数定义。”

与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:

template <> void Swap<int>(int &, int &); // explicit specialization
template <> void Swap(int &, int &);      // explicit specialization

区别在于,这些声明的意思是“不要使用Swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义”。这些原型必须有自己的函数定义。显式具体化声明在关键字template后包含<>,而显式实例化没有。

警告: 试图在同一个文件(或转换单元)中使用同一种类型的显式实例和显式具体化将出错。

还可通过在程序中使用函数来创建显式实例化。例如,请看下面的代码:

template <class T>
T Add(T a, T b) // pass by value
{
    return a + b;
}
...
int m = 6;
double x = 10.2;
cout << Add<double>(x, m) << endl; // explicit instantiation

这里的模板与函数调用Add(x, m)不匹配,因为该模板要求两个函数参数的类型相同。但通过使用Add(x, m),可强制为double类型实例化,并将参数m强制转换为double类型,以便与函数Add(double, double)的第二个参数匹配。

如果对Swap()做类似的处理,结果将如何呢?

int m = 5;
double x = 14.3;
Swap<double>(m, x); // almost works

这将为类型double生成一个显式实例化。不幸的是,这些代码不管用,因为第一个形参的类型为double &,不能指向int变量m。

隐式实例化、显式实例化和显式具体化统称为具体化(specialization)。它们的相同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。

引入显式实例化后,必须使用新的语法——在声明中使用前缀template和template <>,以区分显式实例化和显式具体化。通常,功能越多,语法规则也越多。下面的代码片段总结了这些概念:

...
template <class T>
void Swap (T &, T &); // template prototype
template <> void Swap<job>(job &, job &); // explicit specialization for job
int main(void)
{
  template void Swap<char>(char &, char &); // explicit instantiation for char
  short a, b;
  ...
  Swap(a,b);  // implicit template instantiation for short
  job n, m;
  ...
  Swap(n, m); // use explicit specialization for job
  char g, h;
  ...
  Swap(g, h); // use explicit template instantiation for char
  ...
}

编译器看到char的显式实例化后,将使用模板定义来生成Swap()的char版本。对于其他Swap()调用,编译器根据函数调用中实际使用的参数,生成相应的版本。例如,当编译器看到函数调用Swap(a, b)后,将生成Swap()的short版本,因为两个参数的类型都是short。当编译器看到Swap(n, m)后,将使用为job类型提供的独立定义(显式具体化)。当编译器看到Swap(g, h)后,将使用处理显式实例化时生成的模板具体化。