はじめに
MQL5でEAやインジケーターを開発していると、同じロジックを異なるデータ型で使い回したい場面が頻繁に出てきます。例えば、配列内で特定の値を検索する処理は、int型、double型、string型など、様々な型で必要になります。
従来のMQL5では型ごとに別々の関数を定義する必要がありましたが、テンプレート(template)機能を使うことで、1つの関数定義で複数の型に対応できるようになります。(ジェネリック関数)
本記事では、MQL5のテンプレート機能の基本概念から実践的な使い方まで、ArrayFind関数の実装を通して詳しく解説します。

WAN
チャートのテンプレート(.tpl)ではないよ(‘Д’)
テンプレートとは?
基本概念
テンプレートとは、型をパラメータとして受け取る関数や構造体の雛形です。コンパイル時に実際の型が決定され、その型に応じた関数が自動生成されます。
// テンプレートの基本構文
template<typename T>
戻り値の型 関数名(引数...)
{
// 関数の実装
}
MQL5でのテンプレート宣言
MQL5では以下の方法でテンプレートを宣言できます:
template<typename T> // 最も一般的
template<class T> // typename と同じ意味
template<typename T, typename U> // 複数の型パラメータ
従来の方法とテンプレートの比較
従来の問題点
型別に関数を定義する従来の方法では、以下のような課題がありました:
// 整数用
int ArrayFindInt(int &array[], int value, int start = 0)
{
int size = ArraySize(array);
for(int i = start; i < size; i++)
{
if(array[i] == value) return i;
}
return -1;
}
// 文字列用
int ArrayFindString(string &array[], string value, int start = 0)
{
int size = ArraySize(array);
for(int i = start; i < size; i++)
{
if(array[i] == value) return i;
}
return -1;
}
// double用
int ArrayFindDouble(double &array[], double value, int start = 0)
{
int size = ArraySize(array);
for(int i = start; i < size; i++)
{
if(array[i] == value) return i;
}
return -1;
}
問題点:
- ❌ コードの重複:同じロジックを型ごとに記述
- ❌ 保守性の低下:修正時に全ての関数を更新する必要
- ❌ 関数名の複雑化:型別に異なる関数名を記憶
- ❌ 型の追加時:新しい型用の関数を再実装

WAN
同じことをしている関数なのに、型が違うだけで再定義しないといけないのはとてもナンセンス(‘Д’)
テンプレートによる解決
template<typename T>
int ArrayFind(T &array[], T match_value, int start_pos = 0)
{
int array_size = ArraySize(array);
if(start_pos < 0) start_pos = 0;
if(start_pos >= array_size) return -1;
for(int i = start_pos; i < array_size; i++)
{
if(array[i] == match_value) return i;
}
return -1;
}
テンプレートの利点:
- ✅ コードの統一:1つの定義で全ての型に対応
- ✅ 型安全性:コンパイル時に型チェック
- ✅ 保守性向上:1箇所の修正で全型に適用
- ✅ 直感的使用:どの型でも同じ関数名で使用可能
- ✅ 自動型推論:コンパイラが適切な型を自動選択
ArrayFind関数
関数仕様
template<typename T>
int ArrayFind(
T &array[], // 検索対象の配列
T match_value, // 検索する値
int start_pos = 0 // 検索開始位置(デフォルト:0)
)
引数:
array[]
: 検索対象の配列(任意の型T)match_value
: 検索する値(配列と同じ型T)start_pos
: 検索開始位置(省略可能、デフォルト0)
戻り値:
- 見つかった場合:配列内の位置(0から始まるインデックス)
- 見つからなかった場合:-1
ソースコード
//+------------------------------------------------------------------+
//| ArrayFind.mq5 |
//| Copyright 2025, MQL5 |
//| テンプレートを使用したArrayFind関数 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| ArrayFind テンプレート関数 |
//| 動的配列内で指定された値を検索する汎用関数 |
//+------------------------------------------------------------------+
template<typename T>
int ArrayFind(
T &array[], // 検索対象の配列
T match_value, // 検索する値
int start_pos = 0 // 検索開始位置
)
{
// 配列サイズを取得
int array_size = ArraySize(array);
// 開始位置の妥当性チェック
if(start_pos < 0)
start_pos = 0;
if(start_pos >= array_size)
return -1;
// 指定位置から配列の終端まで検索
for(int i = start_pos; i < array_size; i++)
{
if(array[i] == match_value)
return i; // 見つかった位置を返す
}
return -1; // 見つからなかった場合
}
//+------------------------------------------------------------------+
//| 補助関数:配列の全要素を表示 |
//+------------------------------------------------------------------+
template<typename T>
void PrintArray(T &array[], string array_name = "Array")
{
Print("型名: ", typename(T));
int size = ArraySize(array);
string output = array_name + "[" + IntegerToString(size) + "]: ";
for(int i = 0; i < size; i++)
{
output += (string)array[i];
if(i < size - 1) output += ", ";
}
Print(output);
}

WAN
typename(T)をPrintで出力することで受け取った引数の型が何かまでわかっちゃう!!
サンプルコード
基本的な使用法
void OnStart()
{
Print("=== ArrayFind テンプレート関数のテスト ===");
// 整数配列での検索
int numbers[] = {10, 20, 30, 40, 50, 30, 60};
PrintArray(numbers, "整数配列");
int result = ArrayFind(numbers, 30);
Print("30を検索した結果: ", result, " (値: ", numbers[result], ")");
// 文字列配列での検索
string symbols[] = {"EURUSD", "GBPUSD", "USDJPY", "EURJPY"};
PrintArray(symbols, "通貨ペア配列");
result = ArrayFind(symbols, "GBPUSD");
if(result >= 0)
Print("GBPUSDを検索した結果: ", result, " (値: ", symbols[result], ")");
else
Print("GBPUSDは見つかりませんでした");
// double配列での検索
double prices[] = {1.2345, 1.2350, 1.2340, 1.2355};
PrintArray(prices, "価格配列");
result = ArrayFind(prices, 1.2350);
if(result >= 0)
Print("1.2350を検索した結果: ", result, " (値: ", DoubleToString(prices[result], 4), ")");
}
開始位置を指定した検索
void TestStartPosition()
{
Print("\n=== 開始位置指定検索のテスト ===");
int numbers[] = {10, 20, 30, 40, 30, 50, 30};
PrintArray(numbers, "重複値を含む配列");
int first = ArrayFind(numbers, 30); // 最初から検索
int second = ArrayFind(numbers, 30, 3); // 3番目以降から検索
int third = ArrayFind(numbers, 30, 5); // 5番目以降から検索
int notFound = ArrayFind(numbers, 30, 7); // 配列外から検索
Print("最初の30の位置: ", first);
Print("3番目以降の30の位置: ", second);
Print("5番目以降の30の位置: ", third);
Print("7番目以降の30の位置: ", notFound, " (見つからない)");
}
テンプレートの応用例
1. 複数型パラメータのテンプレート
template<typename T, typename U>
bool ArrayContains(T &array[], U search_value)
{
return ArrayFind(array, (T)search_value) >= 0;
}
void TestMultipleTypes()
{
double prices[] = {1.2345, 1.2350, 1.2340};
// int型の値をdouble配列で検索
bool found = ArrayContains(prices, 1); // 自動的に1.0に変換
Print("1をdouble配列で検索: ", found);
}
2. 構造体でのテンプレート使用
struct PriceData
{
datetime time;
double price;
int volume;
};
template<typename T>
struct ArraySearchResult
{
int index;
T value;
bool found;
};
template<typename T>
ArraySearchResult<T> ArrayFindWithResult(T &array[], T match_value)
{
ArraySearchResult<T> result;
result.index = ArrayFind(array, match_value);
result.found = (result.index >= 0);
result.value = result.found ? array[result.index] : (T)0;
return result;
}
パフォーマンスとベストプラクティス
1. コンパイル時の最適化
テンプレートはコンパイル時に展開されるため、実行時のオーバーヘッドがありません:
// これらの呼び出しは...
ArrayFind(int_array, 30);
ArrayFind(string_array, "EURUSD");
// コンパイル時に以下のような個別関数に展開されます
int ArrayFind_int(int &array[], int match_value, int start_pos);
int ArrayFind_string(string &array[], string match_value, int start_pos);
2. 型制約の追加
template<typename T>
int ArrayFindNumeric(T &array[], T match_value, int start_pos = 0)
{
// 数値型のみに制限したい場合のコメント
// この例では型チェックは実装していませんが、
// 使用時に適切な型を渡すことが重要です
static_assert(sizeof(T) > 0, "型Tは有効な型である必要があります");
return ArrayFind(array, match_value, start_pos);
}
3. エラーハンドリングの強化
template<typename T>
int ArrayFindSafe(T &array[], T match_value, int start_pos = 0)
{
// NULL配列チェック
if(ArraySize(array) == 0)
{
Print("エラー: 配列が空です");
return -1;
}
// 範囲チェック
if(start_pos < 0 || start_pos >= ArraySize(array))
{
Print("エラー: 開始位置が無効です");
return -1;
}
return ArrayFind(array, match_value, start_pos);
}
まとめ
テンプレートの主要なメリット
- コードの再利用性: 1つの実装で複数の型に対応
- 型安全性: コンパイル時の型チェック
- パフォーマンス: 実行時オーバーヘッドなし
- 保守性: 単一箇所での修正が全型に適用
- 拡張性: 新しい型への自動対応
使用時の注意点
- コンパイル時間: 多用するとコンパイル時間が増加
- デバッグ: エラーメッセージが複雑になる場合がある
- 型制約: 演算子や関数が定義されていない型では使用不可
次のステップ
テンプレートを使いこなすことで、より汎用的で保守性の高いMQL5コードが書けるようになります。今回のArrayFind関数を参考に、以下のような関数もテンプレートで実装してみてください:
- ArraySort(配列ソート)
- ArrayMax/ArrayMin(最大値・最小値検索)
- ArrayUnique(重複除去)
- ArrayFilter(条件フィルタリング)
MQL5のテンプレート機能を活用して、より効率的で読みやすいコードを目指しましょう!