テンプレート関数とは
テンプレート関数とは、関数の定義時にデータ型を指定せず、呼び出し時に具体的なデータ型を指定することで、異なるデータ型に対して同じ関数ロジックを適用できる関数のことです。
通常の関数定義とは異なり、データ型の規制がない状態の引数を設定しているので、関数の呼び出し時に具体的なデータ型を指定できるので、より汎用性の高いコードを書くことができます。
以下に、MQL5におけるテンプレート関数の基本的な使い方を説明します。
テンプレート関数の定義
テンプレート関数は、templateキーワードを使用して定義されます。
templateキーワードを使用して関数やクラスを定義することで、テンプレートを扱えるようになります。具体的には、次のようにテンプレートキーワードを記述します:
template<typename T>
T MyFunction(T a, T b)
{
// 関数の定義
}
または、
template<typename T>
class MyClass
{
// クラスの定義
};
このようにすることで、T
という型パラメータを使用して、異なるデータ型に対して同じクラスや関数ロジックを適用できるようになります。
テンプレートを構成する各記述の意味と目的
・template: templateはテンプレートを定義するキーワードです。
・typename: typenameはテンプレートの定義で使用されるキーワードで、テンプレート引数として指定されたものがデータ型であることを示します。
テンプレート引数とは
テンプレート引数は、関数やクラスを定義する際に、その型をパラメータとして受け取ることを可能にします。これにより、同じコードを異なるデータ型に対して再利用することができるようになります。
サンプルコードではT
という名前の型をテンプレート引数として受け取るようにしています。
・<>はテンプレート引数リストを囲むために使用されます。この中にテンプレート引数を指定します。
// テンプレート関数の定義
template<typename T>
T Add(T a, T b)
{
return a + b;
}
// 使用例
void OnStart()
{
int result_int = Add(2, 3); // int型の引数を渡す
double result_double = Add(2.5, 3.5); // double型の引数を渡す
Print("int result: ", result_int); // 結果: 5
Print("double result: ", result_double); // 結果: 6.0
}
この例では、Add関数はテンプレート関数として定義されており、整数型(int型)と浮動小数点型(double型)の両方で使用できる事を示しています。
テンプレートクラスの定義
テンプレートクラスも同様に、templateキーワードを使用して定義されます。以下は、任意の型を扱うテンプレートクラスの例です。
// テンプレートクラスの定義
template<typename T>
class MyClass
{
private:
T value;
public:
MyClass(T v) { value = v; }
void SetValue(T v) { value = v; }
T GetValue() { return value; }
};
// 使用例
void OnStart()
{
MyClass<int> intClass(10); // int型のインスタンスを作成
MyClass<double> doubleClass(3.14); // double型のインスタンスを作成
Print("int value: ", intClass.GetValue()); // 結果: 10
Print("double value: ", doubleClass.GetValue()); // 結果: 3.14
}
補足1:typenameの後に記述可能なテンプレート引数は1つだけ?
、typenameの後に記述可能なテンプレート引数は1つだけではなく、複数の型パラメータを指定することができます。例えば、以下のように複数の型パラメータを持つテンプレートを定義することができます:
// 複数の型パラメータを持つテンプレートクラスの定義
template<typename T, typename U>
class MyClass
{
private:
// プライベートメンバ変数を定義
T value1; // 型Tの変数value1
U value2; // 型Uの変数value2
public:
// コンストラクタの定義
// 引数v1とv2を受け取り、それぞれvalue1とvalue2に初期化する
MyClass(T v1, U v2) : value1(v1), value2(v2) {}
// メンバ変数value1の値を設定するメソッド
void SetValue1(T v1) { value1 = v1; }
// メンバ変数value2の値を設定するメソッド
void SetValue2(U v2) { value2 = v2; }
// メンバ変数value1の値を取得するメソッド
T GetValue1() { return value1; }
// メンバ変数value2の値を取得するメソッド
U GetValue2() { return value2; }
};
// テンプレートクラスの使用例
void OnStart()
{
// MyClassのインスタンスを作成
// int型とdouble型の引数を渡して、コンストラクタを呼び出す
MyClass<int, double> myObject(10, 3.14);
// メンバ変数value1の値を取得して表示
Print("Value1: ", myObject.GetValue1()); // 結果: 10
// メンバ変数value2の値を取得して表示
Print("Value2: ", myObject.GetValue2()); // 結果: 3.14
}
上記のように、template<typename T, typename U>と記述することで、複数のテンプレート引数を持つテンプレートクラスやテンプレート関数を定義することができます。
上記サンプルコードの文法解説
MyClass(T v1, U v2) : value1(v1), value2(v2) {}
この部分は、コンストラクタのメンバ初期化リストを示しています。メンバ初期化リストは、コンストラクタの引数をクラスのメンバ変数に直接初期化するために使用されます。
※コンストラクタについての詳細は↓の記事をご参照ください。
メンバ初期化リストとは?
メンバ初期化リストは、コンストラクタの実行前にメンバ変数を初期化するための構文です。
以下に、メンバ初期化リストの各部分について詳しく説明します。
MyClass(T v1, U v2): これはコンストラクタの宣言です。コンストラクタは、クラスのインスタンスが生成されるときに呼び出されます。この場合、コンストラクタは2つの引数(v1とv2)を受け取ります。
: value1(v1), value2(v2): ここがメンバ初期化リストです。ここで、引数v1の値をメンバ変数value1に、引数v2
の値をメンバ変数value2にそれぞれ初期化しています。これにより、メンバ変数はコンストラクタの本体が実行される前に初期化されます。
{}: これはコンストラクタの本体ですが、ここでは何も実行されていません。すでにメンバ初期化リストで必要な初期化が行われているため、本体が空になっています。
具体例での動作
以下の具体例で、このコンストラクタがどのように動作するかを示します。
void OnStart()
{
// MyClassのインスタンスを作成し、10と3.14で初期化
MyClass<int, double> myObject(10, 3.14);
// メンバ変数value1の値を取得して表示
Print("Value1: ", myObject.GetValue1()); // 結果: 10
// メンバ変数value2の値を取得して表示
Print("Value2: ", myObject.GetValue2()); // 結果: 3.14
}
この場合、myObjectが生成されると、コンストラクタMyClass(int v1, double v2)が呼び出され、引数10がvalue1に、3.14
がvalue2
に初期化されます。その結果、GetValue1メソッドでvalue1の値が10と表示され、GetValue2メソッドでvalue2の値が3.14と表示されます。
メンバ初期化リストを使うと、メンバ変数の初期化が効率的に行われ、コンストラクタの本体で改めて初期化する必要がないため、コードがシンプルになります。
補足2:テンプレートクラスのインスタンス生成時のデータ型指定
テンプレートクラスは、複数の異なるデータ型に対して同じクラス定義を再利用するための機能です。テンプレートクラスをインスタンス化する際には、そのインスタンスで使用する具体的なデータ型を指定する必要があります。これにより、クラスの定義が特定のデータ型に紐づけられます。
※インスタンスについての詳細は↓の記事をご参照ください。
インスタンス生成時特有の文法
テンプレートクラスのインスタンス生成時にデータ型を指定する文法は、C++やMQL5などのテンプレートをサポートする言語に特有のものです。この文法は、テンプレートクラスやテンプレート関数を使用する際に適用されます。
- クラステンプレートのインスタンス化: MyClass<int, double> myObject;
- 関数テンプレートの呼び出し: MyFunction<int, double>(param1, param2);
これにより、テンプレートの型パラメータが具体的なデータ型に置き換えられ、テンプレートの実体が生成されます。
したがって、テンプレートクラスやテンプレート関数を利用する場合に、このようなデータ型指定の文法が用いられます。
補足3:テンプレート利用する際の、データ型指定の必要性について
テンプレートクラスやテンプレート関数は、インスタンス化や呼び出しの際にデータ型を指定する必要があります。しかし、テンプレート関数に関しては、引数から型が推論されるため、呼び出し時に明示的にデータ型を指定しなくてもよい場合があります。
テンプレートクラスの場合
テンプレートクラスの場合、インスタンス生成時にデータ型を指定する必要があります。これは、テンプレートクラスがどのデータ型を使うかを明示的に指定する必要があるためです。
void OnStart()
{
MyClass<int, double> myObject(10, 3.14); // データ型を指定
Print("Value1: ", myObject.GetValue1()); // 結果: 10
Print("Value2: ", myObject.GetValue2()); // 結果: 3.14
}
テンプレート関数の場合
テンプレート関数の場合、引数から型が推論されるため、呼び出し時にデータ型を指定しなくてもよいことがあります。
void OnStart()
{
int result_int = Add(2, 3); // データ型を指定しなくても、引数から型が推論される
double result_double = Add(2.5, 3.5); // 同様に型が推論される
Print("int result: ", result_int); // 結果: 5
Print("double result: ", result_double); // 結果: 6.0
}
テンプレート関数のデータ型推論が働かない場合
特定の状況では、テンプレート関数のデータ型推論が働かないことがあります。この場合、明示的にデータ型を指定する必要があります。
template<typename T>
T Multiply(T a, T b)
{
return a * b;
}
void OnStart()
{
auto result = Multiply<int>(2, 3); // 明示的にデータ型を指定
Print("result: ", result); // 結果: 6
}
まとめると、テンプレートクラスのインスタンス化時には必ずデータ型を指定する必要がありますが、テンプレート関数の呼び出し時には引数からデータ型を推論できるため、指定しなくても使えることが多いです。