l VisualC テンプレート '97.7.17
l.お知らせ 内容 LINK FILE   HTML Win PC Unix MS-DOS C C++ Mfc Java 
目次.C++言語_ 1 クラス 2 派生クラス 5 演算子のオーバーロード クラス(1)
 準備 3 オブジェクト 6 テンプレート 8 etc クラス(2)
 CC++ 4 フレンド・多重継承 7 メモリ確保・例外処理 未使用
テンプレート化関数   テンプレート化関数の用例   テンプレート化クラス   クラスの中にメンバ関数の{定義}を書く  例1   クラスの外にメンバ関数の{定義}を書く  インライン関数の指定   例2 

T テンプレート化関数
テンプレート化関数は、引数の型だけが異なる関数を、ひとつずつ定義する代わりにひとつの関数にまとめたものです。
テンプレート(template=templet : 型取り、型板、定規、梁受け、造船台のくさび)

テンプレート化関数は、グローバル関数にします
後記のテンプレート化クラスを除き、{クラス}の中などには宣言できません。

簡単なサンプルとして、int 型の引数を受取ってそれを表示する関数を考えてみます。
void disp ( int x ) { cout << x; }
この関数は、int 型の引数しか受け取ることができません

すべての型の変数を受け取らせるには次のように宣言します。

void disp( int x ) { cout << x; }
template <class t1>
void disp ( t1 x ) { cout << x; }
普通の関数
テンプレート化関数
<class t1>テンプレートリストと呼ばれ、ひとつの関数で使用する引数の種類を指定します。この例では t1 一種類。
class は、引数の型 t1 が自動的に決められることを表します。
t1 は仮りの型で、char 型が必要なら自動的に char 型になります。
実際には、テンプレート化関数を元にして、具体的な引数型を持った関数が複数作られます。
このように宣言すると、関数 disp() は任意の実引数で呼出すことができます。


複数の引数を使用する関数では、次のように t1 を 複数書きます。
テンプレートリストの中に書いた型は、(関数の引数リスト)の中に必ず書く必要があります。 {関数の定義内容}で使わないのは可。
テンプレート化関数であることが区別できるように、使わなくても引数は省略できません。

同じ型
void disp( int x, int y ) { cout << x << y; }
template <class t1>
void disp ( t1 x , t1 y ) { cout << x << y; }
普通の関数
テンプレート化関数
 x と y が同じ型のとき。
 
違う型
void disp( int x, char y) { cout << x << y; }
template <class t1, class t2 >
void disp ( t1 x , t2 y ) { cout << x << y; }
普通の関数
テンプレート化関数
 x と y に違う型が使用されるとき。
 
固定の型 (char 型の例)
void disp( int x, char y ) { cout << x << y; }
template <class t1, char y >
void disp ( t1 x, char y ) { cout << x << y; }
普通の関数
テンプレート化関数
 y に char 型しか使わないことがわかっているとき。
 特定の型の引数については、テンプレートリストに記述する必要はありません。

引数の数が同じで、テンプレートリストを使わない同じ名前の関数がオーバーロードされている場合は、普通の関数の方が優先的に呼び出されます。
(優先される関数)
void disp ( int x ) { cout << x << x << x << endl; }
template <class t1, >
void disp ( t1 x ) { cout << x << endl; }
普通の関数 優先
テンプレート化関数
上記の関数の呼び出し例
disp ( 5 );
disp ( 'A' );
555 と表示されます。
A と表示されます。

コンパイルされるときに、どの型が実際に使われるのか調べられ、必要な引数型の関数が作られます。
実際に作られた関数はテンプレート関数と呼び、テンプレート化関数はその元になっています。

T テンプレート化関数の用例
#include <iostream.h> /* テンプレート化関数 */
template < class t1 > void disp ( t1 x ) { cout << x << endl; }
void main()
{ char s[20]={"ABC"};
  disp(s);             // 文字列のポインタで呼び出します。
  disp('a');           // 文字で呼び出します。
  disp(5);             // 整数で呼び出します。
  int xx; cin >> xx;
}

T テンプレート化クラス
テンプレート化クラスは、メンバデータの型だけが異なるクラスを、ひとつずつ定義する代わりにひとつのクラスにまとめたものです。
メンバデータの型は、オブジェクトを宣言するときに指定します。

テンプレート化クラスのメンバ関数は、グローバル関数であるテンプレート化関数を、そのクラス専用に宣言します。
従って、テンプレート化クラスのメンバ関数の呼び出しには、OB.C1::func() のようなクラスの指定はできません。(グローバルだから指定の必要がありません。)

テンプレートクラスの宣言は、次のように<テンプレートリスト>を付けて書きます。

class C1 { /**/}
template <class t1, class t2, int x>
class C1 { /**/}
普通のクラスの宣言
テンプレート化クラス
このクラスでは、任意の型 t1 と t2 が使えます。
コンストラクタは、int x を、関数の引数とは別に受け取れます。

T クラスの中に メンバ関数の{定義} を書く場合
テンプレートリストの型(上記の例では t1t2 )は、 以下の例のように、メンバデータの宣言やメンバ関数の定義を行う場合に自由に使用できます。

オブジェクトを宣言する場合は、使用しなくても、 テンプレートリストの項目は全部指定します<class t1, int x> → <char, 5>

T デフォルトコンストラクタを使った例
#include <iostream.h> /* テンプレート化クラス(クラスの中に定義 1) */
template < class t1, int x > class C1    // クラス C2
{ public: t1 s[x];         // データメンバ
          // オブジェクトの宣言時に、t1 → char 、x → 5 になります。
          C1()             // デフォルトコンストラクタ
          { s[0]='A';s[1]='B';s[2]='C';s[3]='\0'; }
};
void main()
{ C1 <char,5> OB;          // クラス C1 のオブジェクト OB1 を宣言
  // コンストラクタ C1 は、int 5 を受け取ることができます。
  cout << OB.s << endl;    // s[5]="ABC" になっているかどうか確認 
  int q; cin >> q;
}

引数付きのコンストラクタを使った例
#include <iostream.h> /* テンプレート化クラス(クラスの中に定義 2) */
template < class t1, class t2 > class C1    // クラス C1
{ public: t1 a; t2 b; t2 c;                  // データメンバ
          void set(t1 i, t2 j) { a=i; b=j; } // メンバ関数
          C1(t2 k) { c=k; }                  // コンストラクタ
};
void main()
{ C1 <int,char> OB( 'C');    // クラス C1 のオブジェクト OB1 を宣言
  OB.set(5, 'B');            // メンバ関数で代入 OB.a=5 OB.b='B'
  cout << OB.a << OB.b << OB.c << endl; // 確認  5 B C
  int q; cin >> q;
}

メンバ関数をオーバーロードするときは
結果的に同じ引数の関数ができないようにします。
次のメンバ関数 func は、実質的に同じ引数になってしまいます。
template<class t1> class C1 { /**/ };
C1< int > OB;
// このようなクラスで、
// このようにオブジェクトを宣言した場合、


void func(int a ){ /**/ } // 固定された引数型のメンバ関数
void func( t1 a ){ /**/ } // テンプレートリストの型を使うメンバ関数
// t1 は、オブジェクトの宣言時に int に決められていますから、ふたつの関数の引数は同じになってしまいます。
// なお後記のように、メンバ関数の定義をクラスの外に書く場合は、このような書式も許されます。

T クラスの外に メンバ関数の{定義} を書く場合
<テンプレートリスト>と、その<項目>を、使用しなくても書きます

次のようなクラスを宣言したとします。

class C1 { /**/ }
template <class t1, class t2, int x>
class C1 { /**/ }
普通のクラスの宣言
テンプレート化クラス
このクラスでは、任意の型 t1 と t2 が使えます。
コンストラクタは、int x を、関数の引数とは別に受け取れます。

テンプレート化クラスのメンバ関数の宣言は次ようにします。
テンプレートリストの型を使用できること以外は、普通のクラスと同じです。
クラスの指定 C1:: を書くときは、
テンプレートリストの項目 <t1, t2, x> も書きます

 テンプレート化クラス  普通のクラス
C1<t1, t2, x>::C1( t1 a, int b);
void C1<t1, t2, x>::func1 ( t1 c );
void C1<t1, t2, x>::func2 ( int d );
 
C1::C1( t1 a, int b);
void C1::func1 ( t1 c );
void C1::func2 ( int d );

関数の定義には、そのクラスのテンプレートリストも書きます。
クラスの指定にそのクラスのテンプレートの指定値を書くのは、メンバ関数の宣言の書式と同じです。
これは、テンプレートリストを使わないメンバ関数にも書く必要があります。
template <class t1,class t2,int x>      C1<t1,t2,x>::C1( t1 a, int b)
{ A=a; B=b; }
template <class t1,class t2,int x> void C1<t1,t2,x>::func1( t2 c )
{ C=c; }
template <class t1,class t2,int x> void C1<t1,t2,x>::func2( int d )
{ D=d;}
// int x は、コンストラクタが、(引数)とは別に受け取ることができる変数です。 

オブジェクトの種類毎にメンバ関数の定義を変更
t1, t2 の型と x の値は、オブジェクトの宣言時に設定しますが、その設定別に、関数の定義を別々の内容にできます。
どのタイプのオブジェクト用の関数であるかの指定は、t1, t2, x に具体的な型と値を記述することで行います。
このとき、パラメータリストを使用しない関数(func2)は、パラメータリストの記述だけは省略できます。
template <class t1,class t2,int x>      C1<t1,t2,x>::C1( t1 a, int b)
{ A=a; B=b; }
template <class t1,class t2,int x> void C1<t1,t2,x>::func1( t2 c )
{ C=c;}
template <class t1,class t2,int x> void C1<int,int,5>::func1( t2 c )
{ C=c+c; }
template < class t1, class t2 > void C1<int, char>::func2( int d )
{ D=d;}
// t1,t2,x を <int,int,5> にして宣言したオブジェクトに対しては、
// func1() は、二番目の func1() が働きます。
// この機能は、メンバ関数の定義をクラスの外に書く場合だけ、使用できるようです。

T インライン関数の指定は、テンプレートリストの後に書きます。
 関数の返り値型の前でも、後でも可。
template<class t1,class t2,int x> inline      C1<t1,t2,x>::C1( t1 a, int b)
{ A=a; B=b; }
template<class t1,class t2,int x> inline void C1<t1,t2,x>::func1( t2 c )
{ C=c; }
template<class t1,class t2,int x> inline void C1<t1,t2,x>::func2( int d )
{ D=d;}
template<class t1,class t2,int x> void inline C1<t1,t2,x>::func2( int d )
{ D=d;}

T クラスの外にメンバ関数の{定義}を書く場合の例
#include <iostream.h> /* テンプレート化クラス(クラスの外に定義) */
template <class t1,class t2,int x> class C1      // クラス C1
{ public : int X; int A; t1 B; t2 C; char D;     // メンバデータ
               C1<t1,t2,x>::C1( t1 a, int b);     // コンストラクタ
          void C1<t1,t2,x>::func1( t2 c );        // メンバ関数
          void C1<t1,t2,x>::func2( int d );
};
template <class t1,class t2,int x>      C1<t1,t2,x>::C1( t1 a, int b)
{ A=a; B=b; X=x; }
template <class t1,class t2,int x> void C1<t1,t2,x>::func1( t2 c )
{ C=c; }
template <class t1,class t2,int x> void C1<int,int,x>::func1( t2 c )
{ C=c; X=X+2; }
template <class t1,class t2,int x> void C1<t1,t2,x>::func2( int d )
{ D=d;}

void main()
{ // オブジェクト作成 ⇒  int X=0; int A=1; int B=2; char C; char D;
  C1<int,char,0> OB(1,2);           // クラス C1 のオブジェクト OB1 を宣言
  OB.func1( 'C' ); // OB.C='C'
  OB.func2( 'D' ); // OB.D='D'
  cout << OB.X << OB.A << OB.B << OB.C << OB.D << endl; // 確認 0 1 2 C D
  // 別なオブジェクト ⇒  int X=5; int A=1; int B=2; int C; char D;
  C1<int,int,5> Ob(1,2);            // クラス C1 のオブジェクト OB1 を宣言
  Ob.func1( 3 );   // OB.C=3 X=X+2 
  Ob.func2( 'D' ); // OB.D='D'
  cout << Ob.X << Ob.A << Ob.B << Ob.C << Ob.D << endl; // 確認 7 1 2 3 D
  int q; cin >> q;
}

T

C++言語 5 演算子のオーバーロード 6 テンプレート 7 メモリ確保・例外処理
 
  mtoga@sannet.ne.jp   登録日 '96. 6.15
URL : http://www.page.sannet.ne.jp/mtoga/index.html