目次
低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。
サンプルコード
my_class.h
#ifndef MY_CLASS_H_
#define MY_CLASS_H_
// 関数テンプレート と同様、
// テンプレートは通常ヘッダファイルにすべてを記述する必要があります。
// ヘッダファイルでの using 使用は好ましくないため std::cout 等とします。
#include <iostream>
template <typename MY_TYPE>
class MyClass
{
public:
MyClass(MY_TYPE val);
MyClass(const MyClass& other); // コピーコンストラクタ
public:
void Say() const;
private:
MY_TYPE m_val;
};
template <typename MY_TYPE>
MyClass<MY_TYPE>::MyClass(MY_TYPE val) :
m_val(val)
{
// m_val = val; // とするよりもシンプル
}
template <typename MY_TYPE>
MyClass<MY_TYPE>::MyClass(const MyClass& other)
// MyClass<MY_TYPE>::MyClass(const MyClass<MY_TYPE>& other) // の省略形
// ↑コンパイラが古くなければ「MyClass<MY_TYPE>」以降の「MyClass」は
// 「MyClass<MY_TYPE>」と解釈されます。
{
// コピーするだけの例
m_val = other.m_val;
}
template <typename MY_TYPE>
void MyClass<MY_TYPE>::Say() const
{
std::cout << "MyClass::Say, " << m_val << std::endl;
}
#endif // #ifndef MY_CLASS_H_
main.cpp
#include <iostream>
#include "my_class.h"
using namespace std;
int main() {
// 関数テンプレートと異なり、型の自動判別は困難な場合
// が多く、無効化されています。明記する必要があります。
MyClass<int> obj(1);
MyClass<int> objCopied = obj;
obj.Say();
objCopied.Say();
return 0;
}
デフォルトテンプレート引数
関数テンプレートとは異なり、クラステンプレートにはテンプレート引数の既定値を記述できます。
my_class.h
#ifndef MY_CLASS_H_
#define MY_CLASS_H_
#include <iostream>
template <typename MY_TYPE = char>
class MyClass
{
public:
MyClass(MY_TYPE val);
public:
void Say() const;
private:
MY_TYPE m_val;
};
template <typename MY_TYPE>
MyClass<MY_TYPE>::MyClass(MY_TYPE val) :
m_val(val)
{
}
template <typename MY_TYPE>
void MyClass<MY_TYPE>::Say() const
{
std::cout << "MyClass::Say, " << m_val << std::endl;
}
#endif // #ifndef MY_CLASS_H_
main.cpp
#include <iostream>
#include "my_class.h"
using namespace std;
int main() {
MyClass<> ch(97);
MyClass<int> i(97);
ch.Say(); //=> MyClass::Say, a
i.Say(); //=> MyClass::Say, 97
return 0;
}
なお、デフォルトテンプレート引数が複数ある場合は以下のように既出のテンプレート引数の値に依存させることができます。
template <typename MY_TYPE = char, typename MY_TYPE_2 = MY_TYPE>
template < typename MY_TYPE = char, typename MY_TYPE_2 = MyOtherClassTemplate<MY_TYPE> >
その際、後者の例では ">" と ">" の間にスペースを入れて、コンパイラが演算子 ">>" と区別できる状態にする必要があります。
テンプレート引数にクラステンプレート
my_class.h
#ifndef MY_CLASS_H_
#define MY_CLASS_H_
#include <iostream>
template <template <typename, typename> class MY_TEMPL>
class MyClass
{
public:
void SetShow();
private:
MY_TEMPL< int, std::allocator<int> > m_val;
};
template <template <typename, typename> class MY_TEMPL>
void MyClass<MY_TEMPL>::SetShow()
{
m_val.push_back(0);
m_val.push_back(1);
std::cout << "MyClass::SetShow(" << m_val.size() << ")" << std::endl;
}
#endif // #ifndef MY_CLASS_H_
main.cpp
#include "my_class.h"
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
int main() {
MyClass<vector> objV;
MyClass<deque> objD;
objV.SetShow(); //=> MyClass::SetShow(2)
objD.SetShow(); //=> MyClass::SetShow(2)
objV.SetShow(); //=> MyClass::SetShow(4)
objD.SetShow(); //=> MyClass::SetShow(4)
return 0;
}
テンプレート引数に整数
my_class.h
#ifndef MY_CLASS_H_
#define MY_CLASS_H_
#include <iostream>
#include <algorithm>
template <int N> // double など小数は指定できない仕様 (環境によってはコンパイルできてしまう)
class MyClass
{
public:
static const int SIZE = N; // 静的メンバ定数
public:
MyClass();
public:
void Show(int i) const;
private:
int m_intarr[SIZE];
};
template <int N>
MyClass<N>::MyClass()
{
std::fill_n(m_intarr, SIZE, 0);
}
template <int N>
void MyClass<N>::Show(int i) const
{
std::cout << m_intarr[i] << std::endl;
}
#endif // #ifndef MY_CLASS_H_
main.cpp
#include "my_class.h"
#include <iostream>
using namespace std;
int main() {
MyClass<8> obj;
for (size_t i = 0, size = MyClass<8>::SIZE; i < size; ++i) {
obj.Show(i);
}
cout << MyClass<8>::SIZE << endl; //=> 8
cout << MyClass<16>::SIZE << endl; //=> 16
return 0;
}
特殊化
テンプレート引数が特定のパターンに合致した場合のために、特殊なテンプレートを用意しておくことができます。これをクラステンプレートの特殊化とよびます。特に、テンプレート引数の一部のみを固定して用意する場合を部分特殊化とよびます。なお、特殊なテンプレートを用意する方法としては、一部メンバのみを用意しておく方法 (一部上書き) とクラステンプレート全体を用意しておく方法 (すべて上書き) の二つがあります。
my_class.h
#ifndef MY_CLASS_H_
#define MY_CLASS_H_
#include <iostream>
#include <utility> // pairのため
template <typename MY_TYPE_1, typename MY_TYPE_2>
class MyClass
{
public:
MyClass(MY_TYPE_1 val1, MY_TYPE_2 val2);
public:
void Show() const;
private:
MY_TYPE_1 m_val1;
MY_TYPE_2 m_val2;
};
template <typename MY_TYPE_1, typename MY_TYPE_2>
MyClass<MY_TYPE_1, MY_TYPE_2>::MyClass(MY_TYPE_1 val1, MY_TYPE_2 val2) :
m_val1(val1),
m_val2(val2)
{
}
template <typename MY_TYPE_1, typename MY_TYPE_2>
void MyClass<MY_TYPE_1, MY_TYPE_2>::Show() const
{
std::cout << m_val1 << ", " << m_val2 << std::endl;
}
// 完全な特殊化 (一部上書き)
template <>
void MyClass<double, double>::Show() const
{
std::cout << "double double: " << m_val1 << ", " << m_val2 << std::endl;
}
// 完全な特殊化 (すべて上書き)
template <>
class MyClass<char, char>
{
public:
MyClass(char val1, char val2) : m_val1(val1), m_val2(val2) {}
public:
void Show() const {
std::cout << "char char: " << m_val1 << ", " << m_val2 << std::endl;
}
private:
char m_val1;
char m_val2;
};
// 部分特殊化 (一部上書き) → 実装できません
// 部分特殊化 (すべて上書き)
template <typename MY_TYPE_2>
class MyClass<char, MY_TYPE_2>
{
public:
MyClass(char val1, MY_TYPE_2 val2) : m_val1(val1), m_val2(val2) {}
public:
void Show() const {
std::cout << "char *: " << m_val1 << ", " << m_val2 << std::endl;
}
private:
char m_val1;
MY_TYPE_2 m_val2;
};
// 部分特殊化 (すべて上書き。複雑な例)
template <typename MY_TYPE_2, typename FIRST, typename SECOND>
class MyClass<std::pair<FIRST, SECOND>, MY_TYPE_2>
{
public:
MyClass(FIRST val1_f, SECOND val1_s, MY_TYPE_2 val2) : m_val1(val1_f, val1_s), m_val2(val2) {}
public:
void Show() const {
std::cout << "pair *: ("
<< m_val1.first << ", " << m_val1.second << "), "
<< m_val2 << std::endl;
}
private:
std::pair<FIRST, SECOND> m_val1;
MY_TYPE_2 m_val2;
};
#endif // #ifndef MY_CLASS_H_
main.cpp
#include "my_class.h"
#include <iostream>
using namespace std;
int main() {
MyClass<int,int> objII(1, 1);
MyClass<double,double> objDD(1.1, 1.1);
MyClass<char,char> objCC(97, 97);
MyClass<char,int> objCI(97, 1);
MyClass<pair<int,int>,int> objPI(1, 1, 1);
objII.Show(); //=> 1, 1 (通常のテンプレート)
objDD.Show(); //=> double double: 1.1, 1.1 (特殊化)
objCC.Show(); //=> char char: a, a (特殊化) ← 部分特殊化したものよりも特殊化したものが優先的に利用されます。
objCI.Show(); //=> char *: a, 1 (部分特殊化)
objPI.Show(); //=> pair *: (1, 1), 1 (部分特殊化)
return 0;
}
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。
関連記事
- ダウンキャスト (C++をもう一度)実行時型情報 RTTI #include <iostream> #include <typeinfo> using namespace std; class MyClass { public: virtual ~MyClass() {} // typeid で正しい RTTI // (RunTime Type Information; 実行時型情報) ...
- 競技プログラミングの基本処理チートシート (C++)限られた時間の中で問題を解くために必要となる、競技プログラミングにおける基本的な処理のチートシートです。競プロにおけるメジャー言語 C++ を利用します。その際 C++11 の機能は利用せず C++03 の機能の範囲内で記述します。 頻度高く定期的に開催されるコンテスト AtCoder Codeforces main.cpp #include <iostream>
- 構造体と列挙体 (C++をもう一度)構造体 #include <iostream> using namespace std; struct MyStruct { char charval; int intval; }; void Show(MyStruct* obj) { cout << obj->intval << endl; } int main() { ...
- Valgrind による C/C++ メモリリーク検出JVM メモリリークでは JDK の jstat や jmap で原因を調査できます。C/C++ では valgrind の Memcheck ツールが利用できます。valgrind には複数のツールが含まれており既定のツールが Memcheck です。他のツールを利用する場合は --tool オプションで指定します。 [簡単な利用例](h
- クラスの基本/初期化 (C++をもう一度)構造体のように初期化する (非推奨) #include <iostream> using namespace std; const int MAX_STR = 16; class MyClass { public: int m_integer; char m_str[MAX_STR + 1]; void Show(); }; void MyClass::Show...