今回はちょっと変えてジェネリックプログラミング。
ジェネリックと聞くと薬で特許が切れるやつ?ということを思いつくが(私だけかもしれないが)
ことプログラミングに関しては、
ジェネリクスとは、プログラミング言語の機能・仕様の一つで、同じプログラムコードで様々なデータ型のデータを処理できるようにするもの。C++言語などではほぼ同様の機能を「テンプレート」(template)という。
ということらしい。
なわけで今回はテンプレートを簡単に勉強。
よく例で出てくるのは足し算をする処理が何度も出てくるので関数化する、というのがすぐに出てくると思います。
1 2 3 |
int SUM(int a,int b){ return a+b; } |
int型の二つの引数を取ってその和を返す関数。これはこれでOKだがしかしdoubleやfloatだとそれ用の関数を用意しないといけない。
なのでそれを対応するやり方としてまずマクロがある。
1 |
#define sum(a,b) a+b |
しかしマクロは使っちゃだめよ、と言われている。その理由としては型安全ではない、型推論もできないとかになる。
また以下の例のマクロは二つの数の掛け算。
1 |
#define mul(a,b) a*b |
しかしこれが
1 |
std::cout << mul(1+1,2+3) << std::endl; |
とすると結果は6となります。
これは
1 |
std::cout << 1+1*2+3 << std::endl; |
とされてしまうためで正しくは
1 |
#define mul(a,b) ((a)*(b)) |
とすると正しく計算される。今回の例では二つの数をかけただけの単純なものだが複雑なものだと括弧がいっぱい出てきて
非常に見づらい、とかあるので使うにしても慎重にならないといけない。
テンプレートを使うとこうなる。
1 2 3 4 |
template<typename T> T SUM(T a,T b){ return a+b; } |
1 2 3 4 5 6 7 |
int AllMul(const std::list<int> &v) { int ret = 1; for(const auto a : v) { result *= a; } return ret; } |
こんなんがあって
1 2 3 4 5 6 7 8 |
template <class T> T AllMul(const std::list<T> &v) { T result = 1; for(const auto a : v) { result *= a; } return result; } |
こうなって
1 2 3 4 5 6 7 |
template <class InputIterator, class T> T AllMul(InputIterator f, InputIterator l, T r) { while(f != l) { r *= *f++; } return r; } |
こうなる。これでstd::listやstd::vectorでも配列でもOK
1 2 3 4 5 6 |
std::list<int> v = {1,2,3,4,5}; std::vector<int> v1 = {1,2,3,4,5}; int v2[5] = {1,2,3,4,5}; std::cout << AllMul(v.begin(),v.end(),1) << std::endl; std::cout << AllMul(v1.begin(),v1.end(),1) << std::endl; std::cout << AllMul(v2,v2+5,1) << std::endl; |
InputIteratorは実際にそういうクラスがあるわけではなく便宜的にわかりやすい名前にしているだけ。
次の位置に移動できる(f++)、指し示すデータを参照(*f)ができれこのテンプレートはOK
テンプレートは関数だけでなくクラスもOK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template <class T> class CSetterGetter { T var; public: void set(T v) { var=v; } T get() { return var; } }; int main() { CSetterGetter<int> sg; sg.set(2); std::cout << sg.get() << std::endl;; } |
に。
ちなみにC++17からはコンストラクタに渡される値によって、クラステンプレートのテンプレート引数を推論できる。
https://cpprefjp.github.io/lang/cpp17/type_deduction_for_class_templates.html
なので
1 |
CSetterGetter(T x_) : var(x_) {} |
とコンストラクタが定義されていた場合
1 |
CSetterGetter sg(1); |
でOK。(当然C++14とかだとエラーになります)