C/C++ テンプレート
C++ のテンプレート
C言語には、テンプレートは無い。テンプレート(Templates)には「関数テンプレート」や「クラステンプレート」などがある。(...ようだ...)
クラステンプレート(Class templates)
クラステンプレートと使い方
サンプルコード
#include <iostream> #include <math.h> using namespace std;
template < class _T > class Math_T { _T val; public: // コンストラクタ Math_T() {} // デフォルトコンストラクタ Math_T(_T in_val) { this->val = in_val; } // オペレータの再定義 _T operator ^ (Math_T obj) { // pow() が double で動くため全て double へキャスト // _T で再キャストしてるが、この例では意味が無い。(結果 doubleni ) double ret = ( _T )pow((double)(this->val),(double)(obj.val)); return ret; } };
#define VAL1 10 #define VAL2 -3 void main() { double ans; Math_T < double > first_val = VAL1; Math_T < double > second_val = VAL2; // 代入して実行 ans = first_val ^ second_val; cout << ans << endl; // 0.001 を表示 // キャストして実行 ans = ( Math_T < double > )VAL1 ^ ( Math_T < double > )VAL2; cout << ans << endl; // 0.001 を表示 }※関数テンプレートと変わらないが、class_name < 型名 > に注意。
※この例は、pow() の仕様上 double で値を返す。引数が int 等でも内部処理 double 戻り値も double となる。
▲上へ [ 編集 ]
関数テンプレート(function templates)
テストデータ用関数のテンプレート化
この頁の目的は、以下のテストデータ作成関数の汎用化である。方法は、テンプレートによる汎用化、オーバーロードによる多重定義がある訳だが、出来れば当然関数テンプレートとしたいところである。
問題点は、小数点を含む場合の処理となる。
・・・ 考え中 ・・・
整数型のみ処理とし、実数は別途オーバーロードさせた関数アダプタを経由することに決定。
... と思ったが、配列の参照渡しか!・・・困った!?
パラメータ増やして、小数点以下の条件処理では・・・?
●以下のように汎用化を実施。
template による改良版
#include <iostream> #include <time.h> // time() #include <process.h> // getpid() #include <math.h> // pow() using namespace std; template < class T > bool testArrData( T* arr_tbl, int arr_elm );
// メイン:テストデータ呼出用 void main() { const int i_elmax = 100000; // 配列要素数 double aiTbl[i_elmax]; // 配列定義 testArrData(aiTbl, i_elmax); // 配列へのテストデータ格納 for(int i=0; i<i_elmax; i++) { // テストデータの表示 cout << i << " " << aiTbl[i] << endl; } }
// 取得乱数の初期設定(呼出関数側の p 受取りのほうが汎用は高い) #define RND_N_DEV 2 // digit:-1:10(単位), 0:1, 1:.1, 2:.01 // -1->10 を -2->100, 2->.01, 5->.00001 を取出す #define D_DIGIT pow( (double)(10), (double)-(RND_N_DEV) ) // パラメータで渡しの場合も、関数版で簡単に修正可 //double d_digit(RND_N_DEV) { // double digit = pow((double)(10), -(RND_N_DEV)); // return digit; //} #define EXPECTED_MIN 0 // 乱数の最小値 #define EXPECTED_MAX (10000+1) // 乱数の最大値+1
template < class T > bool testArrData( T* arr_tbl, int arr_elm ) { // 呼び出し毎に異なる乱数系列を発生 srand( (unsigned)( time(NULL) + _getpid() ) ); for (int i=0; i<arr_elm; i++) { arr_tbl[i] = rand() * rand(); // 0 〜 1,073,676,289 arr_tbl[i] = (int)(arr_tbl[i]) // min〜max 整数の乱数 % (int)( EXPECTED_MAX - EXPECTED_MIN ) + ( EXPECTED_MIN ); arr_tbl[i] = ( T )( arr_tbl[i] * (D_DIGIT); ) // 桁調整(intなら不要) } return true; // 取り合えず 成否に関係なく true を返す。 }※・・・さて・・・> 動作確認済み
※小数点2桁までなら int としてもこのまま動くが、範囲調整をしっかりしないと、min max のどちらかが出なくなる。
※テストデータ生成用なので取り合えずこれで十分。
more generic function
bool testArrData( T* tbl, int elm, double min, double max, int digit ); // &tbl[0] と 要素数 elm を渡し、 // 希望する範囲 min max と 小数点位置 digit を指定 // 配列 tbl へ希望範囲のテストデータを代入して返す。※ここまでやると、実に汎用的。但し文字列には対応しない。
※このテンプレートを、string版でオーバーロードすると動くのか?
テンプレートはここで終了。---> 「C++ ソートへ戻る」。
▲上へ [ 編集 ]
関数テンプレートの雛型
template の template sample
※以下のように仮の型名で関数を定義すると、1つの関数定義で複数の型を利用できる。
- 関数テンプレートの書式
- class は、typename でも良い。
- T は任意の文字でよい。
- 例:numeric_T, struct_T ... など。
- template <class A, class B, class C> 等複数使用可能。
- void fn(T x, int i) 等も可能。
template <class T> T function_a(T x, T y) { T ret_val = ( x + y ) / 2; return ret_val; }
void main() { // int型関数として動作する int ians = function_a(123, 456); // 289 cout << "ians = " << ians << endl; // 289 // double型関数として動作する double dans = function_a(123.4567, 456.7890); // 290.12285 cout << "dans = " << dans << endl; // 290.123 }※関数オーバーロードでも同じことが出来るが、型毎に1つの関数定義が必要になる。
※この例は、計算を伴うため string を使うことが出来ないが、sort 等比較だけなら string も使用可能。
※int,double の他、計算式でエラーを出さなければあらゆる型を1つのテンプレートで処理できる。
※型にとらわれることなく、1つのテンプレート定義で事が足りる。
C++ 利用開始当初に見かけたときは意味不明だったが、こうしてみると案内簡単である。
但し、奥は案外深いようである。現状必要なテンプレートの知識は取り合えず事程度で事足りそうなので、これ以上の詮索はしないこととする。
- 関連用語
- ジェネリックプログラミング(generic programming)
- 「型にとらわれないプログラミング」と言うことらしい。
- インスタンス(instance)
- 実体化した関数。(この場合、テンプレートを元に実体化した...)または、宣言のみの変数の実体化されたオブジェクト(変数、配列)とか、クラスとか...
- 汎用関数(generic function)
- コピペで使い回し可能な程に定型化(?)された関数のこと?(当然、型など自動判別してくれて、あらゆるシーンで汎用的に使える...夢のような...)
- ジェネリックプログラミング(generic programming)
関数のオーバーロード
オーバーロードで同じことが出来る例
int function_a(int x, int y) { int ret_val = ( x + y ) / 2; return (ret_val - 100); } double function_a(double x, double y) { double ret_val = ( x + y ) / 2; return (ret_val + 100); }
void main() { // int型の function_a:結果から 100 減算 int ians = function_a(123, 456); // 289(-100) cout << "ians = " << ians << endl; // 189 // double型 function_a:結果へ 100 加算 double dans = function_a(123.4567, 456.7890); // 290.12285(+100) cout << "dans = " << dans << endl; // 390.123 }※型毎に細かい変更が出来るが、全ての型の関数を定義する手数が掛かる。
※特徴1:DLL の組み込み関数名にするのは難しい。(C言語非互換)
※特徴2:関数名の名前付けが楽ちん!?(同名関数でもパラメータにより、自動で処理対象関数が決定)
※注意:コンパイル後の関数名は、コンパイラが自動的に付け替える。(特徴1の由縁:Cコンパイラはこれを処理できない)
▲上へ [ 編集 ]
double型のテストデータ用関数
以下のテストデータ作成関数は、double型専用であるが、テンプレート化することで汎用的な関数とすることが出来る。sample 1-1
#include <iostream> #include <stdlib.h> // srand() rand() #include <time.h> // time() #include <process.h> // getpid() #include <math.h> // double floor(double n) (切捨て処理) bool testArrData(double* arr_tbl, int arr_elm); // test data の作成
void main() { const int arr_elements = 200; // 配列要素数 double arr_tbl[arr_elements]; // 配列の作成 // □テストデータ代入(関数呼出) testArrData(arr_tbl, arr_elements); // □配列要素を表示 for (int i=0; i<arr_elements; i++) { printf("arr_tbl[%05d] = %9.2f \n", i, arr_tbl[i] ); } }
// ******************************** // テストデータ作成関数(汎用版) // ******************************** // 取得したい乱数範囲のパラメータ指定 #define RND_N_DEV 1 // digit指定:100/.00 10/.0 1/0 #define EXPECTED_MIN 0 // 最小値 #define EXPECTED_MAX 100 // 最大値 // 渡された配列(参照渡し)へ乱数データを格納するだけの関数。 // arr_tbl:配列の値渡しアドレスを受取る。 // arr_elm:Number of elements 要するに要素数を受取る。 bool testArrData(double* arr_tbl, int arr_elm) { // srand((unsigned)(time(NULL)+getpid())); // 毎回異なる乱数を発生 srand(1); // 乱数初期値セット for (int i=0; i<arr_elm; i++) { arr_tbl[i] = rand() * rand(); // 〜 1,073,676,289 arr_tbl[i] = floor(arr_tbl[i]/10); // 桁落し(intなら不要) arr_tbl[i] = (int)(arr_tbl[i]) // 0〜100 擬似乱数取得 % (((EXPECTED_MAX - EXPECTED_MIN) * RND_N_DEV) + 1) + (EXPECTED_MIN * RND_N_DEV); arr_tbl[i] = arr_tbl[i] / (RND_N_DEV); // 桁調整(intなら不要) } // 取り合えず 成否に関係なく true を返す。 return true; }※最小値?最大値?出現頻度に問題有った様におもう。(詳細忘れ、切上げ、切捨ての関係?)
※桁上げすると min の出現頻度減少、切り捨てると max の出現頻度減少?の問題点有り?
▲上へ [ 編集 ]
リンク
内部リンク
- C/C++ C++/CLI C# 関連
- VC++ 2005 Express のインストール
- C/C++ の簡単なプログラム例
- C/C++ ソート(並べ替え)
- C/C++ テストの実行
- C/C++ STL(Standard Template Library)
- 変数・定数
- プログラムの分割/ダイナミックリンクライブラリ など
- その他
- C/C++ その他::書式文字/ESC code など
- VB2005リファレンス(覚え書き)
- SQL文:SQLステートメント
- VBA(VisualBasic for Applications)
外部リンク
- 現在ありません
▲上へ [ 編集 ]
便利メモ
汎用関数(generic function)とヘッダ(.h)ファイル
作成済み汎用関数は、ヘッダファイルとすると管理しやすい。方法:単純に「Header Files」フォルダへ
testArrData.h
#include <time.h> // time() #include <process.h> // getpid() #include <math.h> // pow()
// 取得乱数の初期設定(呼出関数側の p 受取りのほうが汎用は高い) #define RND_N_DEV 2 // digit:-1:10(単位), 0:1, 1:.1, 2:.01 // -1->10 を -2->100, 2->.01, 5->.00001 を取出す #define D_DIGIT pow( (double)(10), (double)-(RND_N_DEV) ) // パラメータで渡しの場合も、関数版で簡単に修正可 //double d_digit(RND_N_DEV) { // double digit = pow((double)(10), -(RND_N_DEV)); // return digit; //} #define EXPECTED_MIN 0 // 乱数の最小値 #define EXPECTED_MAX (10000+1) // 乱数の最大値+1
template < class T > bool testArrData( T* arr_tbl, int arr_elm ) { // 呼び出し毎に異なる乱数系列を発生 srand( (unsigned)( time(NULL) + _getpid() ) ); for (int i=0; i<arr_elm; i++) { arr_tbl[i] = rand() * rand(); // 0 〜 1,073,676,289 arr_tbl[i] = (int)(arr_tbl[i]) // min〜max 整数の乱数 % (int)( EXPECTED_MAX - EXPECTED_MIN ) + ( EXPECTED_MIN ); arr_tbl[i] = ( T )( arr_tbl[i] * (D_DIGIT); ) // 桁調整(intなら不要) } return true; // 取り合えず 成否に関係なく true を返す。 }
として保存すればよい。
使用時は、
#include <iostream> #include "testArrData.h" // これを追加するだけ。 using namespace std; void main() { const int i_elmax = 250; double aiTbl[i_elmax]; testArrData(aiTbl, i_elmax); for(int i=0; i<i_elmax; i++) { //printf(" %6.2f ", (double)(aiTbl[i])); cout << "alTbl[" << i << "] = " << aiTbl[i] << endl; } }
・・・とするだけで、すっきり!
▲上へ [ 編集 ]
ヘッダーファイルを使用したサンプル
std::sort
#include <iostream> #include "testArrData.h" #include <algorithm> using namespace std; void main() { const int elements = 250; int arrtbl[elements]; // int, double など変更可 testArrData( arrtbl, elements ); // テストデータ代入 sort( arrtbl, arrtbl + elements ); // std::sort の実行 for(int i=0; i<elements; i++) { printf( "arrtbl[%4i]=%6.2f \n", i, (double)( arrtbl[i] ) ); // cout << "arrtbl[" << i << "] = " << arrtbl[i] << endl; } }
▲上へ [ 編集 ]
qsort 関数
C言語用標準関数。C++ でテスト。※テンプレート、オーバーロード共に、手詰まり。
#include <iostream> #include "testArrData.h" using namespace std;
// qsort用比較関数 typedef int _IT; int cmp_type(const _IT* x, const _IT* y) { _IT *a, *b; a = (_IT*)x; b = (_IT*)y; printf("ret=%i \n", (*a)-(*b) ); return (*a)-(*b); } int cmp_type(const void* x, const void* y) { int ret; double *a, *b; a = (double*)x; b = (double*)y; //printf("*a=%6.2f *b=%6.2f (*a)-(*b)=%6.2f\n ", // (*a),(*b), (float)(*a)-(*b) ); if( ((*a)-(*b)) > (0.0) ) { ret = 1; } else if( ((*a)-(*b)) == (0.0) ) { ret = 0; } else { ret = -1; } printf("ret=%i \n",ret); return ret; //(*a)-(*b); }
void main() { const int elements = 250; typedef double T; T arrtbl[elements]; testArrData( arrtbl, elements ); qsort( arrtbl, elements, sizeof(T), (int (*)(const void*, const void*))cmp_type); for(int i=0; i<elements; i++) { //printf( "arrtbl[%4i]=%6.2f \n", i, (double)( arrtbl[i] ) ); // cout << "arrtbl[" << i << "] = " << arrtbl[i] << endl; } }※何となく動くことは動くが・・・面倒になってきたので途中で放棄。
参考
単純に汎用関数を作ろうと考えたが、うまくいかない...
殆どの場合、渡す値、返す値のキャストでエラーが出てしまう。
但し、戻り値を以下を参考に、整数で返すようにすれば多分いけると思われる。
- 比較関数
- 戻り値の型:int型
※int型なので実数時比較の際、小数点以下の大小関係に注意。- x<y -> なら、int(<0) な整数:-1 -2 ...
- x>y -> なら、int(>0) な整数:1 2 3 ...
- x=b -> なら、int(0) を返す:0
- 比較関数の呼出し:qsort が自動で呼出して勝手に使う。
- テンプレート:int (*comp)(const void* x, const void* y);
- 比較関数を別途準備する仕様により、複雑(構造体メンバ)な処理にも対応できる物と思われる。
- 戻り値の型:int型
▲上へ [ 編集 ]
2008年07月08日(火) 10:36:41 Modified by cafeboy1