C++ 编译器处理模板的原理

         编译器遇到模板方法定义时,会进行语法检查,但是并不编译模板。编译器无法编译模板定义,因为它不知道使用什么类型。不知道x和y的类型,编译器就无法为x=y这样的语句生成代码。
        编译器遇到一个实例化的模板时,例如Grid<int>myIntGrid,就会将模板定义中的每一个T替换为int,从而生成Grid模板的int版本代码。当编译器遇到这个模板另一个实例时,例如,Grid<SpreadsheetCell> mySpreadsheet,就为SpreadsheetCell生成另一个版本的Grid类。编译器生成代码的方式就像语言不支持模板时程序员编写代码的方式:为每个元素类型编写一个不同的类。
将模板代码分布在多个文件中
       通常情况下,类定义放在一个头文件中,方法放在一个源代码文件中,创建或使用类对象的代码会通过#include来包含对应的头文件,通过链接器访问这些方法代码。模板不按这种方式工作。由于编译器需要通过这些“模板”为实例化类型生成实际的方法代码,因此砸任何使用了模板的源代码文件中,编译器都应该能同时访问模板类定义和方法定义。有好几种机制可以满足这种包含需求
1.将模板定义放在头文件中
         方法定义可与类定义直接放在同一个头文件中。当使用这个模板的源文件通过#include包含这个文件时,编译器就能访问需要的所有代码。
此外,还可以将模板方法定义放在另一个头文件中,然后在类定义的头文件中通过#include包含这个头文件。一定要保证方法定义的#include在类定义之后,否则代码无法编译。
tempale<typename T>
Class Grid
{
 //class definition omitted for brevity.
};
#include “GridDefinition.h”


任何需要使用Grid模板的客户只需要包含Grid.h头文件即可。这种分发有助于分发类定义和方法定义。
2.将模板定义放在源文件中
template < typename T>
Class Grid
{
 //class definition omitted for brevity.
};
#include “Grid.cpp”


    使用这种技术时,一定不要把Grid.cpp文件添加到项目中。
3.限制模板类的实例化
如果希望模板类仅用于某些已知的类型,就可以使用下面的技术。
假定Grid类只能实例化int、double和vetcor<int>,其头文件应如下所示:
template < typename T>
Class Grid
{
 //class definition omitted for brevity.
};


注意在这个头文件中,没有方法定义,末尾也没有#include语句。
这里,需要在项目中添加一个真正地.cpp文件,它包含方法定义,如下所示:
#include”Grid.h”
template < typename T>
Grid<T>::Grid(szie_t,inWidth,size_t inHeight):mWidth(inWidth),mHeight(inHeight)
{
    initializeCellsContainer();
}
// Other method...


为了使这个方法能运行,需要给允许客户使用的类型显示实例化模板。.cpp文件结尾应如下所示:
//Explicit instantiations for the types you want to allow
template class Grid<int>;
    template class Grid<double>
    template  class Grid<std::vector<int>>


    有了这些显示的实例化,就不允许客户代码给其他类型使用Grid类模板。
模板参数
    在Grid示例中,Grid模板包含一个模板参数:保存在网格中元素的类型。编写这个类模板时,在尖括号中指定参数列表,如下所示:
template <typename T>
    这个参数列表类似于函数或方法中的参数列表。与函数或方法一样,可以任意多个模板参数编写类。此外,这些参数未必是类型,而且可以有默认值。
1.非类型的模板参数
    非类型的参数是“普通”参数,例如int和指针:即在函数和方法中熟悉的那种参数。然而,非类型的模板参数只能是整数类型(char、int、long...)、枚举类型、指针和引用。
template <typename T,size_t WIDTH, size_t HEIGHT>
#pragma once
template <typename T, size_t WIDTH, size_t HEIGHT>
class Grid
{
public :
	Grid();
	virtual ~Grid();
	void setElementAt1(size_t x, size_t y, const T & inElement);
	T& getElement(szie_t x, size_t y);
	const T& getElement(size_t x, size_t y);


	size_t getHeight() const { return HEIGHT; };
	size_t getWidth() const { return WIDTH; };
private:
	T mCells[WIDTH][HEIGHT];


};
template<typename T, size_t WIDTH, size_t HEIGHT>
inline Grid<T, WIDTH, HEIGHT>::Grid()//Zero-initialize mCells
{
	//Nothing to do
}
template<typename T, size_t WIDTH, size_t HEIGHT>
inline Grid<T, WIDTH, HEIGHT>::~Grid()
{


}
template<typename T, size_t WIDTH, size_t HEIGHT>
inline void Grid<T, WIDTH, HEIGHT>::setElementAt(size_t x, size_t y, const T & inElement)
{
	mCells[x][y] = inElement;
}


template<typename T, size_t WIDTH, size_t HEIGHT>
inline T & Grid<T, WIDTH, HEIGHT>::getElement(szie_t x, size_t y)
{
	return mCells[x][y];
}


template<typename T, size_t WIDTH, size_t HEIGHT>
inline const T & Grid<T, WIDTH, HEIGHT>::getElement(size_t x, size_t y)
{
	return mCells[x][y];
}


    可通过以下方式实例化这个模板:

Grid<int, 10, 10> myGrid;
Grid<int, 10, 10> anotherGrid;
myGrid.setElementAt(2, 3, 45);
anotherGrid = myGrid;
cout << anotherGrid.getElementAt(2, 3);

    不能通过非常量的整数指定高度或宽度,下面的代码无法编译:
size_t height = 10;
Grid<int, 10, height>testGrid;
    把height声明为const,这段代码就可以编译了:
const size_t height = 10;
Grid<int, 10, height>testGrid;
带有正确返回类型的constexpt函数也可以编译。例如,如果有一个返回size_t的constexpr函数,就可以使用它初始化height模板参数:
    constexpr size_t getHeight() { return 10; }
    Grid<double, 2, getHeight()> myDoubleGrid;


2.类型参数的默认值
    可以为高度和宽度非类型模板参数提供默认值
template<typename T = int, size_t WIDTH = 10, size_t HEIGHT = 10>
class Grid
{
	//Remainder is identical to the previous version.
};


不需要再方法定义的模板规范中指定T、WIDTH、HEIGHT的默认值。例如,下面是serElementAt()的实现:
template <typename T,size_t WIDTH,size_t HEIGHT>
void Grid<T, WIDTH, HEIGHT>::setElementAt(size_t x, size_t y, const T& inElem)
{
	mCells[x][y] = inElem;
}

现在实例化Grid可以不指定模板参数、只指定一个元素类型、指定元素类型和宽度,或者指定元素类型、宽度和高度:

Grid<> muIntGrid;
Grid<int> myGrid;
Grid<int, 5> anotherGrid;
Grid<int, 5, 5>aFourthGrid;



猜你喜欢

转载自blog.csdn.net/u011206291/article/details/52931789