Function Templates
函数模板
重载函数通常用于对各种数据类型执行类似的操作。ArraySize()是MQL4中此类函数的简单示例。它返回任何类型数组的大小。实际上,这个系统函数是重载的,这种重载的整个实现对MQL4应用程序开发者是隐藏的:
int ArraySize(
void& array[] // checked array
);这意味着MQL4语言编译器会在每次调用此函数时插入必要的实现。例如,对于整数类型数组,可以这样实现:
int ArraySize(
int& array[] // array with int type elements
);ArraySize()函数可以以下列方式显示,用于处理历史数据格式中的报价:MqlRates类型数组:
int ArraySize(
MqlRates& array[] // array filled with MqlRates type values
);因此,使用同一个函数来处理不同类型非常方便。然而,必须进行所有准备工作——必要的函数应该对所有数据类型进行重载,以便能够正确地处理这些类型。
有一个方便的解决方案。如果需要对每种数据类型执行类似的操作,可以使用函数模板。在这种情况下,程序员只需编写一个函数模板描述。在描述模板时,我们应该只指定一些形式参数,而不是函数应处理的特定数据类型。编译器将根据调用函数时使用的参数类型自动生成各种函数来处理每种类型。
函数模板定义以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()函数仅返回最高索引值,可以用来获取实际值。例如:
//--- create an array
double array[];
int size=50;
ArrayResize(array,size);
//--- fill with random values
for(int i=0;i<size;i++)
{
array[i]=MathRand();
}
//--- find position of the highest value in the array
int max_position=ArrayMaximum(array);
//--- now, get the highest value itself in the array
double max=array[max_position];
//--- display the found value
Print("Max value = ",max);因此,我们分两步获取数组中的最高值。使用ArrayMax()函数模板,只需将适当类型的数组传递给此函数,即可获得所需类型的结果。这意味着,除了最后两行
//--- find position of the highest value in the array
int max_position=ArrayMaximum(array);
//--- now, receive the highest value itself in the array
double max=array[max_position];我们现在只需要一行代码,其中返回的结果与传入函数的数组类型相同:
//--- find the highest value
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));
//---
}
//+------------------------------------------------------------------+
//| Type is returned as a line |
//+------------------------------------------------------------------+
template<typename T>
string GetTypeName(const T &t)
{
//--- return the type as a line
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不允许将字符串转换为布尔值。我们可以自己实现这一点——让我们创建一个模板函数的重载版本。例如:
//+------------------------------------------------------------------+
//| Template function |
//+------------------------------------------------------------------+
template<typename T1,typename T2>
string Assign(T1 &var1,T2 var2)
{
var1=(T1)var2;
return(__FUNCSIG__);
}
//+------------------------------------------------------------------+
//| Special overload for 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"));
}代码执行结果显示,Assign()模板函数被用于int+字符串对,而重载版本在第二次调用时已经被用于bool+字符串对。
string Assign<int,string>(int&,string)
string Assign(bool&,string)