最近更新したページ
2013-10-20
2013-09-29
2013-09-23
2012-01-07
2011-11-09
2011-10-23
2011-10-09
2011-10-01
2011-09-29
2011-09-03
2011-08-07
2011-08-02
2011-07-29
2011-07-10
2011-05-05
2011-05-04
2011-04-24
2011-04-13
2011-04-05
2011-03-26
2011-02-18
2011-02-15
2010-12-26
2010-12-07
2010-12-05
2010-11-23
2010-09-28
2010-09-23
2010-08-26
2010-08-22
2010-07-16
2010-01-17
2010-01-11
2009-10-04
2009-08-21
2009-08-13
2009-06-18
2009-06-01
2009-04-29
2009-02-16
2009-02-11
2009-02-03
2008-07-22
2008-07-21
2008-07-15
2008-07-14
2008-07-13
2008-07-12
2008-07-08
2008-07-05
2008-06-28
2008-06-17
2008-06-05
2008-06-02
2008-06-01
2008-05-29
2008-05-26
2008-05-21
2008-05-19
2008-05-18
2007-10-31
2007-10-27
2007-09-28
2007-09-23
2007-09-17
2007-09-16
2007-09-14
2007-09-11
2007-06-18
2007-04-15
2006-12-21
2006-11-30
2006-11-22
2006-08-17
2006-03-29
2006-03-28
2006-03-27

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)
      • コピペで使い回し可能な程に定型化(?)された関数のこと?(当然、型など自動判別してくれて、あらゆるシーンで汎用的に使える...夢のような...)

関数のオーバーロード


オーバーロードで同じことが出来る例
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 の出現頻度減少?の問題点有り?

▲上へ [ 編集 ]

リンク


内部リンク


外部リンク


  • 現在ありません

▲上へ [ 編集 ]

便利メモ


汎用関数(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);
    • 比較関数を別途準備する仕様により、複雑(構造体メンバ)な処理にも対応できる物と思われる。

▲上へ [ 編集 ]
2008年07月08日(火) 10:36:41 Modified by cafeboy1




スマートフォン版で見る

×

この広告は60日間更新がないwikiに表示されております。