LoginSignup
32
30

More than 5 years have passed since last update.

templateの概念からTMPの応用まで

Last updated at Posted at 2014-02-13

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、便利な機能があって
「ほう」
「たとえばさっきのvectorintの時だけ最適化した実装にしたいとする

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_typeintならintになる
おk?」
typedef?」

typedef int my_type ;

で、my_typeintの別名として使える」
「ああ、そういうことねなるほど理解」
「*nixのaliasみたいなの(型バージョン)
うん

struct some_struct
{
    typedef hoge_type hogehoge_type ;
} ;

だと、some_struct::hogehoge_typehoge_typeの別名になる
hoge_typeintならintだし、doubleならdouble
ここまではおk?」
「おけ」
「うん
で、これはもちろんtemplatestructclassに対しても行える

template < typename T >
struct some_struct
{
    typedef hoge_type hogehoge_type ;
} ;

みたいな」
「ほう」
「そして、こんなこともできる

template < typename T >
struct some_struct
{
    typedef T hogehoge_type ;
} ;


「やっぱりできるんだ!(予想してた」
「うん
で、
これとtemplateの特殊化を組み合わせると、例えば「Tintなら○○、Tdoubleなら△△、hoge < T >なら□□
とかもできる」
「すげー(感嘆」
「うん
さて、これあるものに似ているんだけど、わかる?」
「うーん、なんか既視感あるんだけど」
「関数」
「あ!、なるほど」
Tを引数として考えて、メンバtypedefを戻り値として考えると、まんま関数なわけ
ただし引数と戻り値はそれぞれ型
こうして考えると、構造体は関数に似たものとして考えることができる
という発想で行うプログラミングを、Template Meta Programmin、略してTMP」
「だから、型でプログラミング(?)してるなって感じたのかな」
「おー
その感覚イイゾー」
「C++たのしい!」
「これがそれなりに複雑になると例えばこんなこともできる
http://qiita.com/minamiyama1994/items/b8f1185ed93b6835c239
Cなんかのswitch文の高級バージョン(Haskellなんかにあるパターンマッチ文)をtemplateで実現しちゃおうっていうやつ
TMPでゴニョゴニョすればこういうこともできる」
「やばい(やばい)」
「このへんかな、あ、そうそう、大事なこと忘れてた
こういうTMPとかってなんのために行うか、何だけど」
「fm」

たとえばさっきのvectorintの時だけ最適化した実装にしたいとする

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型のオブジェクトt1T2型のオブジェクトt2t1 + t2ってやると型として、HogeType < T1 , T2 >が手に入る
で、このHogeType < T1 , T2 >を元に最適化をやったり、特殊な処理を行ったり、ってのができる
これなんか変態的な例かな
http://www.kmonos.net/alang/boost/classes/lambda.html

「む」
find_iffor_eachの第3引数には普通関数を入れるんだけど、関数を即席の式で作っちゃえ、というライブラリ」
「演算子オーバーロードって演算子の拡張?(混乱」
「そうそう
ユーザ定義型に対して演算子を再定義してやる
で、ここでの_1とかはライブラリ側が定義したユーザ定義型を持ってるから、演算子オーバーロードが使える」
「なるほど」
「で、それとExpression TemplateとTMPを使って、即席で関数を作ってる
_1%5==0 && _1>30
なら「第一引数が5の倍数かつ30より大きい場合に真を返す」関数を即席で作ってる」
「lambda的な?」
「そうそうまさしくlambda式
と言うヤバいことも出来るよ、というお話でした」
「なるほどな、C++やばいすごい」
「やったぜ」

32
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
30