alpha_kai_NET氏との会話
「あ、テンプレート is 何ってのが」
「あー、テンプレートか。オーバーロードってあるじゃん?」
「例えば
void abc(int arg)
と
void abc(char arg)
を別個に定義できるやつですよね」
「そうそう。
オーバーロードで例えばこういうのがあるとする
int add ( int x , int y )
{
return x + y ;
}
double add ( double x , double y )
{
return x + y ;
}
add
という名前でint
版とdouble
版がある」
「ほうほう」
「で、これをfloat
にも対応させたい
こうなる
int add ( int x , int y )
{
return x + y ;
}
double add ( double x , double y )
{
return x + y ;
}
float add ( float x , float y )
{
return x + y ;
}
」
「ふむふむ」
今度はlong
にも
こうなる
int add ( int x , int y )
{
return x + y ;
}
double add ( double x , double y )
{
return x + y ;
}
float add ( float x , float y )
{
return x + y ;
}
long add ( long x , long y )
{
return x + y ;
}
」
「もしかして:コードが長くなるのを解消する機能」
「そそ
これが例えば誰かの作ったUserDefineInt
とかにも対応させたい、とかなったらキリがない
というわけで
template < typename T >
T add ( T x , T y )
{
return x + y ;
}
これでおk」
「あー、型を推論してくれるのかな?」
「そうそう
add(1,2)
ッて書いたらadd<int>
が推論されるし
add(1.0,2.5)
ッて書いたらadd<double>
が推論される
これらはコンパイラが勝手に導出してくれる」
「かなり便利そう(C言語マン」
「yes
めちょべんり
で、これはクラスにも使える」
「fmfm」
「
class vector_int { /* ... */ } ;
class vector_long { /* ... */ } ;
class vector_double { /* ... */ } ;
とか延々あったら面倒くさいので
template < typename T >
class vector { /* ... */ } ;
こう書いておけば」
「(C++のクラスよく知らない)」
「あー
とりあえずstruct
の強化版って思ってたら間違いない」
「まー、感覚的には」
「さて
となるとこっちのほうがいいかな
struct vector_int { /* ... */ } ;
struct vector_long { /* ... */ } ;
struct vector_double { /* ... */ } ;
これをこう書き換えれば
template < typename T >
struct vector { /* ... */ } ;
vector < int >
やvector < double >
とかvector < UserDefineType >
とかできる
いちいちそれぞれの型ごとに作ってやらなくても良くなる」
「! 最強じゃないか!(小並感)」
「いぇあ
ちなみにtemplate
、便利な機能があって
「ほう」
「たとえばさっきのvector
でint
の時だけ最適化した実装にしたいとする
template < typename T >
struct vector { /* ... */ } ;
template < >
struct vector < int > { /* ... */ } ;
こうすればint
バージョンだけ作れる
hoge < T >
の時だけ最適化した実装を作りたい
template < typename T >
struct vector { /* ... */ } ;
template < typename T >
struct vector < hoge < T > > { /* ... */ } ;
こうすればおk」
「基本はtemplate
で指定した型だけ強化、なるほど」
「そうそう、基本的に共通の実装を提供するんだけど、特定のパターンの型だけ特殊化できる
じゃあ、ここからすこしだけTMPの話入るけど続けるよ?
C++ではstruct
の中でtypedef
ができるってのがある
要するにこうできる
struct some_struct
{
typedef hoge_type hogehoge_type ;
} ;
使うときはsome_struct::hogehoge_type
とかやる
構造体やクラスのメンバとしてtypedef
を定義できる、ということ」
「えーと、まず、Cのstruct
が曖昧ってのが(」
「あー、今はとりあえずCの構造体は変数をまとめるものだと思ってたらいいと思うし実際そう
で、C++だとそれに加えて関数やtypedef
もまとめれる、ということ」
「なるほどー、関数を持てる構造体的な?」
「そうそう。typedef
も持てる
で、typedef
の場合はsome_struct::hogehoge_type
みたいに使える
hoge_type
がint
ならint
になる
おk?」
「typedef
?」
「
typedef int my_type ;
で、my_type
がint
の別名として使える」
「ああ、そういうことねなるほど理解」
「*nixのalias
みたいなの(型バージョン)
うん
で
struct some_struct
{
typedef hoge_type hogehoge_type ;
} ;
だと、some_struct::hogehoge_type
がhoge_type
の別名になる
hoge_type
がint
ならint
だし、double
ならdouble
ここまではおk?」
「おけ」
「うん
で、これはもちろんtemplate
なstruct
やclass
に対しても行える
template < typename T >
struct some_struct
{
typedef hoge_type hogehoge_type ;
} ;
みたいな」
「ほう」
「そして、こんなこともできる
template < typename T >
struct some_struct
{
typedef T hogehoge_type ;
} ;
」
「やっぱりできるんだ!(予想してた」
「うん
で、
これとtemplate
の特殊化を組み合わせると、例えば「T
がint
なら○○、T
がdouble
なら△△、hoge < T >
なら□□
とかもできる」
「すげー(感嘆」
「うん
さて、これあるものに似ているんだけど、わかる?」
「うーん、なんか既視感あるんだけど」
「関数」
「あ!、なるほど」
「T
を引数として考えて、メンバtypedef
を戻り値として考えると、まんま関数なわけ
ただし引数と戻り値はそれぞれ型
こうして考えると、構造体は関数に似たものとして考えることができる
という発想で行うプログラミングを、Template Meta Programmin、略してTMP」
「だから、型でプログラミング(?)してるなって感じたのかな」
「おー
その感覚イイゾー」
「C++たのしい!」
「これがそれなりに複雑になると例えばこんなこともできる
http://qiita.com/minamiyama1994/items/b8f1185ed93b6835c239
Cなんかのswitch
文の高級バージョン(Haskellなんかにあるパターンマッチ文)をtemplate
で実現しちゃおうっていうやつ
TMPでゴニョゴニョすればこういうこともできる」
「やばい(やばい)」
「このへんかな、あ、そうそう、大事なこと忘れてた
こういうTMPとかってなんのために行うか、何だけど」
「fm」
「
たとえばさっきの
vector
でint
の時だけ最適化した実装にしたいとするtemplate < typename T > struct vector { /* ... */ } ; template < > struct vector < int > { /* ... */ } ;
こうすれば
int
バージョンだけ作れる
hoge < T >
の時だけ最適化した実装を作りたいtemplate < typename T > struct vector { /* ... */ } ; template < typename T > struct vector < hoge < T > > { /* ... */ } ;
こうすればおk
こういうのあったじゃん?」
「おう、ありましたな」
「ここでhoge < T >
のときに最適化というのがあるのだけれど
ここがもっと複雑で「T
が○○で△△な条件を満たしているかもしくは~」
みたいな複雑な条件の場合に最適化、というのを考えると、TMPが使える」
「ほう」
「あと、Expression Templateという技法がある」
「ほう」
「C++には演算子オーバーロードっていうのがあって、ユーザ定義型に対して演算子(+
、-
、*
、/
など)を普通の関数みたいにオーバーロードできるって機能なのだけれど」
「ほうほう」
「
template < typename T1 , typename T2 >
HogeType < T1 , T2 > operator + ( T1 t1 , T2 t2 ) ;
こういうのを考えてみると、T1
型のオブジェクトt1
とT2
型のオブジェクトt2
でt1 + t2
ってやると型として、HogeType < T1 , T2 >
が手に入る
で、このHogeType < T1 , T2 >
を元に最適化をやったり、特殊な処理を行ったり、ってのができる
これなんか変態的な例かな
http://www.kmonos.net/alang/boost/classes/lambda.html
」
「む」
「find_if
やfor_each
の第3引数には普通関数を入れるんだけど、関数を即席の式で作っちゃえ、というライブラリ」
「演算子オーバーロードって演算子の拡張?(混乱」
「そうそう
ユーザ定義型に対して演算子を再定義してやる
で、ここでの_1
とかはライブラリ側が定義したユーザ定義型を持ってるから、演算子オーバーロードが使える」
「なるほど」
「で、それとExpression TemplateとTMPを使って、即席で関数を作ってる
_1%5==0 && _1>30
なら「第一引数が5
の倍数かつ30
より大きい場合に真を返す」関数を即席で作ってる」
「lambda的な?」
「そうそうまさしくlambda式
と言うヤバいことも出来るよ、というお話でした」
「なるほどな、C++やばいすごい」
「やったぜ」