C++コンパイラがテンプレートを処理する原理

5380 ワード

コンパイラがテンプレートメソッド定義に遭遇すると、構文チェックが行われますが、テンプレートはコンパイルされません.コンパイラはテンプレート定義をコンパイルできません.なぜなら、どのタイプを使用するか分からないからです.xとyのタイプが分からないと、コンパイラはx=yのような文にコードを生成できません.
コンパイラがインスタンス化されたテンプレートに遭遇すると、たとえばGridmyIntGridは、テンプレート定義の各Tをintに置き換え、Gridテンプレートのintバージョンコードを生成します.コンパイラがこのテンプレートの別のインスタンスに遭遇すると、たとえばGrid mySpreadsheetは、SpreadsheetCellの別のバージョンのGridクラスを生成します.コンパイラがコードを生成する方法は、言語がテンプレートをサポートしていないときにプログラマがコードを記述する方法のように、要素タイプごとに異なるクラスを記述します.
テンプレートコードを複数ファイルに分散する
通常、クラス定義はヘッダファイルに配置され、メソッドはソースファイルに配置され、クラスオブジェクトのコードを作成または使用すると、#includeによって対応するヘッダファイルが含まれ、リンクを介してこれらのメソッドコードにアクセスします.テンプレートはこのように動作しません.コンパイラは、これらのテンプレートを使用してインスタンス化タイプの実際のメソッドコードを生成する必要があるため、テンプレートを使用したソースコードファイルを破壊すると、コンパイラはテンプレートクラス定義とメソッド定義に同時にアクセスできるはずです.このようなニーズを満たすいくつかのメカニズムがあります
1.テンプレート定義をヘッダファイルに配置する
メソッド定義は、クラス定義と直接同じヘッダファイルに配置できます.このテンプレートのソースファイルを#includeで含めると、コンパイラは必要なすべてのコードにアクセスできます.
さらに、テンプレートメソッド定義を別のヘッダファイルに配置し、クラス定義のヘッダファイルに#includeでヘッダファイルを含めることもできます.メソッド定義のincludeがクラス定義後であることを保証しなければなりません.そうしないと、コードはコンパイルできません.
tempale
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のみをインスタンス化できると仮定し、ヘッダファイルは次のようになります.
template < typename T>
Class Grid
{
 //class definition omitted for brevity.
};

注意このヘッダファイルには、定義方法がなく、末尾にも#include文がありません.
ここでは、プロジェクトに本格的な追加が必要である.cppファイルです.メソッド定義が含まれています.以下に示します.
#include”Grid.h”
template < typename T>
Grid::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;
    template class Grid
    template  class Grid<:vector>>

これらの表示のインスタンス化により、クライアントコードが他のタイプにGridクラステンプレートを使用することは許可されません.
テンプレートパラメータ
Gridの例では、Gridテンプレートには、グリッドに保存された要素のタイプというテンプレートパラメータが含まれます.このクラステンプレートを作成する場合は、次のようにカッコ内でパラメータのリストを指定します.
template
このパラメータリストは、関数またはメソッドのパラメータリストに似ています.関数やメソッドと同様に、任意の複数のテンプレートパラメータでクラスを記述できます.また、これらのパラメータは必ずしもタイプではなく、デフォルト値があります.
1.非タイプのテンプレートパラメータ
非タイプのパラメータはintやポインタなどの「一般」パラメータ、すなわち関数やメソッドでよく知られているパラメータです.ただし、非タイプのテンプレートパラメータは整数タイプ(char、int、long...)、列挙タイプ、ポインタ、および参照.
template
#pragma once
template 
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
inline Grid::Grid()//Zero-initialize mCells
{
	//Nothing to do
}
template
inline Grid::~Grid()
{


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


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


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

このテンプレートは、次のようにインスタンス化できます.
Grid myGrid;
Grid anotherGrid;
myGrid.setElementAt(2, 3, 45);
anotherGrid = myGrid;
cout << anotherGrid.getElementAt(2, 3);

非常に多くの整数で高さまたは幅を指定することはできません.次のコードはコンパイルできません.
size_t height = 10;
GridtestGrid;
heightをconstと宣言すると、このコードはコンパイルできます.
const size_t height = 10;
GridtestGrid;
正しい戻りタイプを持つconstexpt関数もコンパイルできます.たとえば、size_を返すものがある場合tのconstexpr関数を使用すると、heightテンプレートパラメータを初期化できます.
    constexpr size_t getHeight() { return 10; }
    Grid myDoubleGrid;
2.タイプパラメータのデフォルト値
高さと幅の非タイプテンプレートパラメータのデフォルト値を指定できます.
template
class Grid
{
	//Remainder is identical to the previous version.
};

再メソッド定義を必要としないテンプレート仕様でT、WIDTH、HEIGHTのデフォルト値を指定します.たとえば、以下はserElementAt()の実装です.
template 
void Grid::setElementAt(size_t x, size_t y, const T& inElem)
{
	mCells[x][y] = inElem;
}

インスタンス化Gridでは、テンプレートパラメータを指定せずに、1つの要素タイプのみを指定したり、要素タイプと幅を指定したり、要素タイプ、幅、高さを指定したりできます.
Grid<> muIntGrid;
Grid myGrid;
Grid anotherGrid;
GridaFourthGrid;