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
