Русский

Справочник MQL4 Основы языка Объектно-ориентированное программирование Шаблоны функций

Шаблоны функций

Перегруженные функции обычно используются для выполнения похожих операций над различными типами данных. Простой пример такой функции в MQL4 - ArraySize(), которая возвращает размер массива любого типа. На самом деле эта системная функция является перегруженной, и вся реализация такой перегрузки спрятана от разработчика программ на MQL4:

int  ArraySize(
   void&  array[]      // проверяемый массив
   );

То есть на самом деле компилятор языка MQL4 подставляет для каждого вызова этой функции нужную реализацию, например, для массивов целого типа примерно так:

int  ArraySize(
   int&  array[]      // массив с элементами типа int
   );

А для массива типа MqlRates для работы с котировками в формате исторических данных функцию ArraySize() можно представить таким образом:

int  ArraySize(
   MqlRates&  array[] // массив, заполненный значениями типа MqlRates
   );

Таким образом, очень удобно использовать одну и ту же функцию для работы с разными типами, но необходимо самостоятельно провести всю предварительную работу, а именно – сделать перегрузку нужной функции для всех типов данных, с которыми она должна будет корректно работать.

Есть более удобное решение – если для каждого типа данных должны выполняться идентичные операции, то более компактным и удобным решением является использование шаблонов функций. При этом программисту нужно написать всего одно описание шаблона функции. При таком описании шаблона в качестве параметра достаточно указать не конкретный тип данных, с которыми должна работать функция, а некий формальный параметр. Основываясь на типах аргументов, использованных при вызове этой функции, компилятор будет автоматически генерировать разные функции для соответствующей обработки каждого типа.

Определение шаблона функции начинается с ключевого слова template, после которого в угловых скобках идет список формальных параметров. Каждый формальный параметр предваряется ключевым словом typename. Формальные типы параметров – встроенные типы или типы, определенные пользователем. Они используются:

  • для задания типов аргументов функции,
  • для задания типов возвращаемого значения функции,
  • для объявления переменных внутри тела описания функции

 

Количество параметров в шаблоне не может быть больше восьми. Каждый формальный параметр в определении шаблона должен хотя бы один раз появиться в списке параметров функции. Каждое имя формального параметра должно быть уникальным.

Вот пример шаблона функции для поиска максимального значения в массиве любого числового типа (целые и вещественные числа):

template<typename T>
T ArrayMax(T &arr[])
  {
   uint size=ArraySize(arr);
   if(size==0) return(0);          
   
   T max=arr[0];
   for(uint n=1;n<size;n++)
      if(max<arr[n]) max=arr[n];
//---
   return(max);
  }

Данный шаблон описывает функцию, которая находит максимальное значение в переданном массиве и возвращает его в качестве результата. Напомним, что встроенная в MQL4 функция ArrayMaximum() возвращает только индекс найденного максимального значения, по которому пользователь в дальнейшем может получить уже и само значение. Например, так:

//--- создадим массив
   double array[];
   int size=50;
   ArrayResize(array,size);
//---  заполним случайными значениями
   for(int i=0;i<size;i++)
     {
      array[i]=MathRand();
     }
 
//--- найдем позицию максимального элемента в массиве
   int max_position=ArrayMaximum(array);
//--- теперь получим само максимальное значение в массиве
   double max=array[max_position];
//--- вывод найденного значения
   Print("Max value = ",max);

Таким образом, нам понадобилось два действия, чтобы получить максимальное значение в массиве. С помощью шаблона функции ArrayMax() можно получить результат нужного типа, просто передав в неё массив соответствующего типа. То есть вместо двух последних строчек

//--- найдем позицию максимального элемента в массиве
   int max_position=ArrayMaximum(array);
//--- теперь получим само максимальное значение в массиве
   double max=array[max_position];

теперь мы можем использовать одну строку, возвращающую сразу результат того же типа, что и переданный массив:

//--- найдем максимальное значение
   double max=ArrayMax(array);

При этом тип результата, возвращенный функцией ArrayMax(), будет автоматически соответствовать типу массива.

 

Для создания универсальных способов работы с различными типами данных необходимо использовать ключевое слово typename для получения типа аргумента в виде строки. Покажем это на примере функции, которая возвращает в виде строки тип данных:

#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 
   CTrade trade;   
   double d_value=M_PI;
   int i_value=INT_MAX;
   Print("d_value: type=",GetTypeName(d_value), ",   value=", d_value);
   Print("i_value: type=",GetTypeName(i_value), ",   value=", i_value);
   Print("trade: type=",GetTypeName(trade));
//--- 
  }
//+------------------------------------------------------------------+
//| Возвращает в строковом виде тип                                  |
//+------------------------------------------------------------------+
template<typename T>
string GetTypeName(const T &t)
  {
//--- вернем тип в виде строки
   return(typename(T));
//---
  }

 

Шаблоны функций можно также использовать и для методов класса, например:

class CFile
  {
   ...
public:
   ...
   template<typename T>
   uint WriteStruct(T &data);
  };
 
template<typename T>
uint CFile::WriteStruct(T &data)
  {
   ...
   return(FileWriteStruct(m_handle,data));
  }

Шаблоны функций нельзя объявлять с ключевыми словами export, virtual и #import.

Перегрузка шаблонных функций

В некоторых случаях может понадобиться перегрузка шаблонной функции. Например, у нас есть шаблонная функция, которая записывает в первый параметр значение второго параметра  с помощью явного приведения типов. В языке MQL5 запрещено приведение типа string к типу bool, мы можем сделать это сами – для этого создадим перегрузку шаблонной функции. Например:

//+------------------------------------------------------------------+
//| Шаблонная функция                                                |
//+------------------------------------------------------------------+
template<typename T1,typename T2>
string Assign(T1 &var1,T2 var2)
  {
   var1=(T1)var2;
   return(__FUNCSIG__);
  }
//+------------------------------------------------------------------+
//| Специальная перегрузка для случая bool+string                    |
//+------------------------------------------------------------------+
string Assign(bool &var1,string var2)
  {
   var1=(StringCompare(var2,"true",false) || StringToInteger(var2)!=0);
   return(__FUNCSIG__);
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int i;
   bool b;
   Print(Assign(i,"test"));
   Print(Assign(b,"test"));
  }

В результате выполнения данного кода мы увидим, что для пары int+string  была использована шаблонная функция Assign(), а при втором вызове для пары bool+string уже иcпользовалась перегруженная версия.

string Assign<int,string>(int&,string)
string Assign(bool&,string)

Смотри также

Перегрузка