プログラミング > C++ > テンプレートに関するメモ

C++ テンプレートに関するメモ


テンプレートは関数やクラスで使う

※テンプレートはここに書いてない仕様もたくさんあるらしく、とても難しいわ
※テンプレートインライン関数だとかデフォルトテンプレート引数だとか
※特殊化とか部分特殊化とかテンプレート引数にテンプレートとか 他にもたくさん! 難しすぎる

使い方は関数やクラスの宣言や定義の直前に
template <class Type1, class Type2> 
もしくは
template <typename Type1, typename Type2> 
を書き込む。
Type1やType2の部分は分かりやすい好きな文字にすればよいとのこと(もちろん予約キーワードなどに重なったらまずいが)
class と typename のキーワード区別は特にないので好きなほうを使えばいいとのこと
(クラス定義のclassと混同しやすいしtypenameのほうを使ったほうがいいのかな)

テンプレートだけを分割コンパイルはできない!
テンプレートとは読んで字のごとくで、コンパイラにとってのテンプレートである
テンプレートはコンパイル時にコンパイラが使われてる型の分だけテンプレートに従って作り直す
  1. template <class Type> void swap(Type& value1, Type& value2) {
  2. Type temp = value1; value1 = value2; value2 = temp;
  3. }
  4.  
  5. int main() {
  6. int a = 10, b = 20;
  7. double x = 5.5, y = 7.7;
  8. swap(a, b);
  9. swap(x, y);
  10. return 0;
  11. }
とあったらコンパイラがソースコードを次のように展開したかのようにコンパイルする
  1. void swap(int& value1, int& value2) {
  2. int temp = value1; value1 = value2; value2 = temp;
  3. }
  4. void swap(double& value1, double& value2) {
  5. double temp = value1; value1 = value2; value2 = temp;
  6. }
  7.  
  8. int main() {
  9. int a = 10, b = 20;
  10. double x = 5.5, y = 7.7;
  11. swap(a, b);
  12. swap(x, y);
  13. return 0;
  14. }
つまり、コンパイラが自動でオーバーロードを作ってくれるみたいなもんである(あくまで”みたいな”)
コンパイラが作りなおせるようにテンプレートの関数やクラスを使う場合は
その定義(関数の中身とか)を毎回、全部書いてやる必要がある
通常はヘッダファイルに全部書いておく
(また、短い関数ならインライン展開される場合もある)

関数の例
  1. //関数の宣言
  2. template <typename Type>
  3. Type max(Type value1, Type value2);
  4. //関数の定義
  5. template <typename Type>
  6. Type max(Type value1, Type value2) {
  7. return value1 > value2 ? value1 : value2;
  8. }
  9. //利用
  10. int main() {
  11. int a = 10, b = 200, c, d;
  12. double x = 1.0, y = 5.5, z;
  13. c = max(a, b);
  14. z = max(x, y);
  15. d = max<int>(a, y); // maxの定義だと引数はどちらも同じ型である必要があるので
  16. // テンプレート引数で型を指定することで両方がint型として使えるらしい
  17. return 0;
  18. }
テンプレート引数はこのほかにも
関数の引数の数がテンプレート引数の定義の数よりも少ない場合なども指定してやる必要がある
  1. template <typename Type1, typename Type2, typename Type3>
  2. Type1 add(Type2 value1, Type3 value2) {
  3. return value1 + value2;
  4. }
  5.  
  6. int main() {
  7. int a, b;
  8. a = add<int>(1.56, 2.3); //この場合、引数からは分からない戻り値の型を指定する
  9. b = add<int, double, float>(5.6, 123.3); //全部指定したほうが本当は親切である
  10. return 0;
  11. }

クラスの例
  1. //クラスの宣言
  2. template <typename Type> class MyClass;
  3. //クラスの定義
  4. template <typename Type>
  5. class MyClass
  6. {
  7. Type value;
  8. public:
  9. MyClass(Type t) { value = t; }
  10. Type& getValue() { return value; }
  11. void setValue(Type t) { value = t; }
  12. Type& addValue(Type t);
  13. };
  14. template <typename Type>
  15. Type& MyClass<Type>::addValue(Type t) {
  16. value += t;
  17. return value;
  18. }
  19. //継承
  20. class MyClassInt : public MyClass<int>
  21. {
  22. };
  23. template <typename Type>
  24. class MyClass2 : public MyClass<Type>
  25. {
  26. };
  27. //利用
  28. int main() {
  29. MyClass<long> mylong(123);
  30. MyClass<double> *mydouble = new MyClass<double>(1.23);
  31. mylong.setValue(999);
  32. return 0;
  33. }

※かなり特殊な使い方もできるとのこと
テンプレート引数の定義をtypenameやclassでなく実際の型を指定するやりかたである
  1. template <int iVal>
  2. int add(int value) {
  3. return value + iVal;
  4. }
  5. int main() {
  6. int a, b;
  7. a = add<123>(5); // add で 5 + 123 が計算される
  8. b = add<999>(777); // add で 777 + 999 が計算される
  9. return 0;
  10. }
テンプレート引数の種類によってコンパイラがオーバーロードのようなものをを作ってしまうので
123用のadd と 999用のadd の2つが作られてしまう
このような使い方にどのようなメリットがあるのかは不明
(コンパイル時の解釈だから型か数値リテラルくらいしか指定できないと思われ)
以下のような使い方ができるらしい
  1. template <class Type, int SIZE>
  2. class Array
  3. {
  4. Type arr[SIZE]; // 静的メモリが確保できる!
  5. public:
  6. Array() {}
  7. };
  8.  
  9. int main() {
  10. Array<int, 100> array;
  11. return 0;
  12. }





  1.  



最終更新:2013年10月25日 19:35