显示实例化
包含模型能够确保所有需要的模板都已经实例化。这是因为:当需要进行实例化的时候,C++编译系统会自动产生对应的实例化体。另外,C++标准还提供了一种手工实例化模板的机制:显示实例化指示符(explicit instantiation directive)。
1 显示实例化的例子
为了说明手工实例化,让我们回顾前面那个导致链接器错误的例子。在此,为了避免这个链接其错误,我们可以通过给程序添加下面的文件:
myfirstinst.cpp
#include "myfirst.cpp"
//基于类型double显示实例化print_typeof()
template void print_typeof<double>(double const &);
显示实例化指示符由关键字template和紧接其后的我们所需要实例化的实体(可以是类、函数、成员函数等)的声明组成,而且, 该声明是一个已经用实参完全替换参数之后的声明。在我们的例子中,我们针对的是一个普通函数,但该指示符也适用于成员函数 和静态数据成员。避如:
//基于int显式实例化MyClass<>的构造函数
template MyClass<int>::MyClass();
//基于int显式实例化函数模板max()
template int const& max(int const &, int const &);
你还可以显式实例化类模板,这样就可以同时实例化它的所有类成员,但有一点需要注意:对于这些在前面已经实例化过的成员,就不能再次对它们进行实例化:
//基于int显式实例化类Stack<>
template class Stack<int>
//基于string显式实例化Stack<>的某些成员函数
template Stack<std::string>::Stack();
template void Stack<std::string>::push(std::string const &);
template std::string Stack<std::string>::top() const;
//错误:对于前面已经显式实例化过的成员函数,不能再次对它进行显式实例化
template Stack<int>::Stack();
对于每个不同实体,在一个程序中最多只能有一个显式实例化体,换句话,你可以同时显式实例化print_typeof<int>和print_typeof<double>,但在同一个程序中每个指示符都只能够出现一次。如果不遵循这条规则,通常都会导致链接错误,链接器会报告:发现了实例化实体的重复定义。
人工实例化有一个显著的缺点:我们必须仔细跟踪每个需要实例化的实体。对于大项目而言,这种跟踪很快就会带来巨大负担;因此,我们不建议使用这种方法。事实上,我们曾经在几个大项目刚开始时就低估了这种负担,而等到代码快要完成的时候,我们就为使用人工实例化而后悔不已。
然而,显式实例化还是有它自身的一些优点的,实例化可以在需要的时候才进行。显然,我们因此避免包含庞大头文件的开销,更可以把模板定义的源文件封装起来;但封装之后,客户端程序就不能基于其他类型来进行额外的实例化了。另外,对于某些程序,精确控制模板实例的准确位置也是很有用的,显式实例化就可以做到这一点;而如果使用自动实例化的话,这种精确位置控制是不可能的(细节请参见第10章)。
2 整合包含模型和显式实例化
为了让程序员能够根据实际情况,自由地选择包含模型或者显式实例化,我们可以把模板的定义和模板的声明放在两个不同的文件中。通常的做法是使用头文件来表示两个文件(头文件大多是那些希望被#include、具有特定扩展名的文件);通常而言,遵守这种文件分 开约定是明智的(因此,我们最前面例子中的myfirst.cpp文件,现在将命名为myfirstdef.h,由预处理器来检测这些被插入的代码)。下面所示的基于Stack<>类模板阐明了这一点。
//stack.h
#ifndef STACK_H
#define STACK_H
#include <vector>
template <typename T>
class Stack {
private:
std::vector<T> elems;
public:
Stack();
void push(T const &elem);
void pop();
T top() const;
};
#endif // STACK_H
//stackdef.h
#ifndef STACKDEF_H
#define STACKDEF_H
#include "stack.h"
#include <stdexcept>
#include <iostream>
template <typename T>
Stack<T>::Stack()
{
elems.clear();
}
template <typename T>
void Stack<T>::push(T const &elem)
{
elems.push_back(elem);
}
template <typename T>
void Stack<T>::pop()
{
if(elems.empty())
{
std::out_of_range("Stack<T>:: pop() empty stack");
}
elems.pop_back();
}
template <typename T>
T Stack<T>::top() const
{
if(elems.empty())
{
throw std::out_of_range("Stack<T>::top() empty stack");
}
return elems.back();
}
#endif // STACKDEF_H
具体的使用示例包含模型
//stacktest1.cpp
#include <iostream>
#include "stackdef.h"
int main()
{
try {
Stack<int> intStack;
intStack.push(5);
intStack.push(69);
std::cout << "intStack.top()========" << intStack.top() << std::endl;
intStack.pop();
std::cout << "intStack.top()========" << intStack.top() << std::endl;
intStack.pop();
std::cout << "intStack.top()========" << intStack.top() << std::endl;
} catch (std::exception const &ex) {
std::cerr << "exception ====" << ex.what() << std::endl;
}
std::cout << "Hello World!" << std::endl;
return 0;
}

显示实例化模板
//stack_inst.cpp
#include "stackdef.h"
#include <string>
template Stack<int>::Stack();
template Stack<std::string>::Stack();
template void Stack<std::string>::push(std::string const &elem);
template std::string Stack<std::string>::top() const;
int main()
{
Stack<int> intStack;
intStack.push(96);
std::cout << "intStack.top========" << intStack.top() << std::endl;
Stack<double> doubleStack;
doubleStack.push(73.8);
std::cout << "doubleStack.top=====" << doubleStack.top() << std::endl;
Stack<float> floatStack;
floatStack.push(65.3f);
std::cout << "floatStack.top======" << floatStack.top() << std::endl;
Stack<std::string> stringStack;
stringStack.push("hello");
stringStack.push("world");
std::cout << "stringStack.top=====" << stringStack.top() << std::endl;
return 0;
}

本文深入探讨C++中的模板实例化,包括自动实例化和显示实例化的过程及优缺点。通过具体示例,讲解如何在需要时进行精确控制,避免链接错误,以及如何整合包含模型和显示实例化。
&spm=1001.2101.3001.5002&articleId=107318333&d=1&t=3&u=40669f01221f48f090f1fa12a59da84a)
2874

被折叠的 条评论
为什么被折叠?



