■ Template する場所を見抜く方法
[ 処理が同じ ]で [ Data 型が異なる ]ところ.
-> 逆に関数とは, 特定の処理をひとくくりにすること.
Instanciate した時点で既に, template ではないことに注目.
次のような記述もえきる。
struct Tbl
{
string name;
void *(*create)();
};
Tbl tbl[] = {
{
{"Chara", create< Chara > },
}
値を交換する処理は、型が違うだけで処理は同じ。
オーバーロードした瞬間にテンプレートが利用できないか考えること。
コンパイラにやらせてしまう。
int swap( int &a, int &b ) {
int t = a;
a = b;
b = t;
}
float swap( float &a, float &b ) {
float t = a;
a = b;
b = t;
}
型をパラメータ化して関数のテンプレートをつくる。
template < typename T>
void swap( T& a, T& b )
{
T t = a;
a = b;
b = t;
}
swap(x, y); // コンパイラが自動で生成する。
swap< int>(x, y); // 型を明示する場合は int をつける。
■ 関数テンプレート(FunctionTemplate)
SYNTAX
[ class | typename ] keyword 利用する。
template < class type>
function-declarator
template < typename type>
function-declarator
template < class X1 , class X2>
void println(X1 var1 , X2 var2);
関数テンプレートには2種類の場所で定義できる。
// template 型であることの宣言
template< class T >
T myabs( T a ){
a = a>0 ? a : -a;
}
// 使用するときは
int b = -7;
int n = myabs( b );
// 明示する
int b = -7;
int n = myabs< int>( b );
PrmTpl が返値のみでも OK.
-> ただしどの型に対して Instanciate するべきか教える必要がある.
-> 関数の後に < >() とあったら, PrmTpl を 明示していると考えよう.
template < typename T >
T foo()
{
T data = 0;
return data;
}
Client::main() {
int i = func< int>();
}
WARNING
引数が複数ある場合は、同一の型がくるとしても template 名を分ける必要あり
template< class T1, class T2 >
T1 Add( T1 a, T2 b ){
return a+b;
}
int a = 5;
char b = 3;
int n = add( a, b );
■ クラステンプレート(ClassTemplate)
DESC
意味としては、クッキーの型だと思えばOK
素材( 型 )と処理を分離して考える
内部的な型だけを変換していると思えばOK , あくまでクラス定義しているにすぎない
次のように考えるとわかりやすい
template< XXX > を除去
2. XXX -> 具体的な型に変換
[ vector ] を例に考えてみる.
vector< myInt > foo; // int 型 vec ( 使用する際は, < 型> とする )
template < T>
class vector{
T foo;
}
// 変換後( こう見ればよい )
class vector{
int foo;
}
// 使用する際は
vector< int > aaa; // [ int型 vector クラスの aaa を生成した ] と考える
■ 型だけでなく 値もパラメータとして受け取れる
非型パラメータを指定する場合は型名は実際の型にする。
// 固定サイズの配列
template < typename T, int N>
class Array {
public:
void push_back( const T &data ) {
data[ p ] = data;
p ++;
}
Array() : p(0){}
private:
T data[N];
int p;
};
使うときは非型パラメータの部分は値を指定する。
// int 型 10 要素の固定配列
Array< int, 10> a;
a.push_back( 5 );
整数型・列挙型の値
オブジェクトへのポインタ・参照
関数へのポインタ・参照
メンバへのポインタ
POINT
template の型を固定することもできる
//
// unsigned にすることで,負数を避けることが可能.
// アラインメントをそろえる
template< unsigned int BASE, typename T >
T floor( T x )
{
return static_cast< T>( ((x) + ((BASE)-1)) & ~((BASE)-1) );
}
// 利用する時は
roundUp< 16>( 10 );
■ テンプレートパラメータにデフォルト値を設定する
デフォルト値を指定するには、デフォルト引数と同様に = TYPE とする。
STL クラスはアロケータのデフォルトとして std::allocator< T> をもつ。
template < class T, class Allocator = allocator< T> >
class vector
{
...
}
そのため利用するときは、要素型を指定するだけでよい
自作のアロケータがある場合は、指定することでメモリ処理をカスタマイズできる。
class MyAllocator {
};
vector< int, MyAllocator > v;
■ template 化するコンパイラの立場で考えてみる
template < class T> // 以下は汎用クラスだよ. ( 未知の型がくるよ. とりあえず T にするよ )
template < [typelist] [ , [ arglist]] > declaration
パラメータ化した汎用関数、または汎用クラスだよー と compiler に通知.
declaration : 関数またはクラスを宣言
typename nameType
未知の識別子の[ 型 ]だよ- とコンパイラに通知( class keyword でも OK )
■ 特殊化する
DESC
ある特定のテンプレート型の場合だけ特別仕様にする時につかう。
特定の template 引数用に定義すること.
-> 特殊化する前に、汎用 template を定義する必要あり
XXX 型の場合の特別バージョンという意味。
template< >
void func< XXX >{}
特化するには, 汎用のテンプレート( 定義 | 宣言 )が必要になる。
// 基本は CMP 単位で処理されて,
// あ ! foo という FunctionTemplate があるな...
template < typename T > T foo();
// これは float 型の場合に利用する関数だな...
template < >
float foo< float>()
{
float data = 0.0f;
printf("special");
return data;
}
Client::main() {
// foo はたしか, FunctionTemplate で float 型の特殊定義があったな...
// よし今回はこれを使おう.
foo< float >();
}
引数の数は合わせる必要があるらしい.
宣言のみ && 特化の場合は CMP は通るが, LINK で落ちる.
ie. 特化は別 Module でしてもいいことになる.
-> 要は, FunctionTemplate の宣言は, パラメータ数のみが一致すれば CMP OK な汎用宣言だと考えよう.
template < typename T>
void copy(T* dst, const T* src, size_t size)
{
for(size_t i = 0; i < size; i++) {
dst[i] = src[i];
}
}
// 特殊化( 型を指定しない -> < > , 逆に template< > とある場合は, template 本体がある. )
// XXX の場合と言い換えよう
template < >
void copy< char>(char* dst, const char* src, size_t size)
{
memcpy(dst, src, size);
}
template < >
static inline ErrorCode DEPRECATED createInterface( pad **unknown, uint32_t no)
{
return interfacebase::impl::createInterfaceImpl(unknown, no);
}
// 部分特殊化
// 元となるテンプレート
template < typename T1, typename T2, int I>
class A { ... };
// 部分特殊化したもの
template < typename T, int I>
class A< T, T*, I> { ... };
template < typename T1, typename T2, int I>
class A< T1*, T2, I> { ... };
template < typename T>
class A< int, T*, 5> { ... };
■ template 全般
Class, 関数の定義をパラメータ化する ( Java にない便利機能 )
定義を抽象化( Grouping )する.
通常は. 宣言と定義を header にする。
宣言のみでは LINK できない。
コンパイル時にコードを生成するため、コンパイル単位で定義が必要になるため。
基本的に define 同様の置換がされるが,
PreProcessor ではなくコンパイラがコードを生成する
汎用 class( 型 )だけでなく, 固定型も使用可能.
= をつけると Default 設定. ( = とでてきたら Default で [い.く.つ] と考えること )
実際に使用して == ( PrmTpl )を与えて, 評価されるらしい.
// MethodTemplate も作成できる
class Foo {
public:
template< typename T >
void func( T i )
{
cout < < i < < endl;
}
};
foo.func( 10.0f );
foo.func( 10 );
foo.func( "10" );
FunctionTemplate と 通常関数が 同名ならば, 通常関数が優先される.
-> ただし TypeCheck して固定関数にあうものがなければ, FunctionTemplate から生成されるよ.
template< typename T >
void func( T i )
{
cout < < i < < endl;
}
// 固定関数.
void func( int i )
{
cout < < "fixfunc " < < i < < endl;
}
{
unsigned int i = 10;
func( i ); // FunctionTemplate がよばれる.
}
前方宣言も OK
bCls が ClassTemplate の場合, bCls の変数はクラスを明示しないとアクセスできない.
( 言語仕様. GCC は準拠. VS++ では無視. )
class Object {
// ctor という Method を template 化した
template< typename T >
Object( T ty ){ ... }
};
int i;
Object o( i );
template < class T, int N = 1> // Default 1 && Arg2 は int であると CMP に通知.
class Allocator {
}
-> FuncTemplate には利用できないらしい. ( なんで ? )
Template 内容に矛盾があれば, コンパイル時にエラーがおきる
// SmartPtr を 実装.
class Resource
{
public:
void use(){ /*参照カウンタ+1*/ }
void release(){ /*参照カウンタ-1と解放*/ }
};
template < class T>
class SmartPointer
{
T *m_pointer; // 本体.
public:
// INIT
SmartPointer(T *p)
:m_pointer(p) {
{
// 置換した状態で矛盾があれば CmpErr
m_pointer->use();
}
~SmartPointer() {
m_pointer->release();
}
}
■ template 具体例
DESC
setter, getter の template 化
// prop object
template < class T> // 以下は汎用クラスだよ. ( 未知の型がくるよ. とりあえず T という名前にするよ ^/ )
class typeProp : public absProp
{
public:
virtual eProp getType() const;
// accessor という処理は同じ.
vitual T getVal( base *p ) = 0;
vitual void setVal( base *p, T val ) = 0;
};
■ 色々実験してみる
Q: PrmTpl を引数にとらない関数は FuncTemplate にできるか ?
template< typename T >
void func()
{
T i = 20;
printf( i );
}
func< int>(); // OK: 文として矛盾が起きないなら問題なし.
func(); // ERR: TemplateParamter がわからない.
< T > と教えてあげる.
func< Foo >(); // ERR: ここで本当に instantiated しているらしい.
Foo i = 20; // ERR
func(); // ERROR: no matching function for call to `func()' func が具体化されていいない証拠.
■ refcnt : 参照カウンタ
DESC
object 自身が他から, 参照される個数をカウントする.
0 になれば, 不要ということで解放. -> new した時点で 1.
POINT
2. create() 作成 && release() 破棄
3. Model から参照されている際に、削除できないようにリソースという扱いにしている
( ie. new, delete < -> create(), release() なんだ )
4. mdl->release() は mdl というポインタ( 矢印 )から Object(Resource)を離すイメージ
// delete のかわり、[ 参.照.し.な.い.よ ] と宣言.
virtual void release(){
counter --;
if ( counter == 0 ){
delete this;
}
}
// これを呼ばないとだめなのが, メンドイ.
void use(){
counter ++;
}
// 作成時に cnt = 1
Resource() : m_counter(1), m_name(0) {
}
WARNING
obj が相互に 参照する場合は, 循環がおき, mem leak がおきるので注意
任意の数値を文字列に変換する関数テンプレート。
#include < iostream>
#include < string>
#include < sstream>
template< class T>
string numberToString(T n)
{
stringstream s;
s < < n;
return ss.str();
}