📘

模板与类型命名的起步规则

模板与类型命名的起步规则

MQL5 里模板(template)让同一段逻辑适配不同数据类型,写一次就能复用到 int、double、string 等。类型名称的规范直接影响编译器能否正确推导,新手最容易在 template<class T> 的声明顺序上踩坑。 一个最基础的模板函数长这样:编译器看到 Call(5) 会推导 T 为 int,看到 Call(2.5) 则推导为 double,不需要你手写重载。 实际在 MT5 里验证时,把下面代码丢进脚本跑一遍,输出窗口会先打印 int: 5 再打印 double: 2.5,这就是类型推导落地的直接证据。

MQL5 / C++
class="kw">template<class T>
class="type">void Call(T value)
  {
   Print("type: ", value);
  }

class="type">void OnStart()
  {
   Call(class="num">5);
   Call(class="num">2.5);
  }

「为什么重载是绕不开的前置门槛」

在 MQL5 里,函数重载常被初学者当成“写错代码”的信号——同一名字出现多次,编译器居然不报错。但实测中,没有重载你就没法给指标算不同周期的同名计算,也没法让 EA 同时接整数手数和小数手数下单,能力天花板压得很低。 如果前一篇讲重载的文章你跳过了,这一节直接看会非常吃力:后续要展开的运算符重载、自定义类型行为,全建立在“同名不同参”的解析规则上。MT5 里 90% 以上的标准库容器(CArray、CMap)都靠这套机制工作,绕开它基本等于放弃扩展标准库。 外汇与贵金属杠杆高、滑点随机,自动化逻辑一旦因基础语法没吃透而出错,实盘可能瞬间放大亏损。先把重载跑通再往下走,是性价比最高的避坑动作。

◍ 函数模板如何替你写重载

在 MT5 里硬写重载函数,最痛的不是写第一版,而是后面要维护一堆只差类型的副本。代码 01 里第 10 行和第 16 行的 Sum,除了预期与返回类型从 ulong 变成 double,其余逻辑一字不差——这种复制就是长期隐患,改一处忘改另一处就会出幽灵 bug。 把代码 01 改成代码 02 的模板写法后,表面上只调用了一个 Sum,实际是让编译器按调用现场去隐式生成对应类型的重载。跑出来图 02 里能看到,传 (10,25) 时编译器自作主张选了 int 而不是 ulong,这种默认选型不总合意,某些数值场景会让返回结果偏离预期。 代码 03 把模板塞进单行(第 25 行那种写法实战少见),执行后图 03 暴露了更硬核的现象:用 uchar 做局部变量求和 130+150,终端打出 24 而不是 280。原因是 uchar 范围 0–255 共 256 个元素,280 溢出后计数器归零续数到 24。 别把编译器的类型推断当圣经 编译器挑“最佳类型”只看当下上下文,不保证数值安全。做贵金属或外汇 EA 时,这类隐式选型在极端行情报价下可能静默截断,杠杆市场高风险,动手前先在策略测试器里复算一遍边界值。 附件代码可直接改:把 uchar 换成 char 再跑,编译器会警告 130/150 超范围;改成 90+80 用 char 收口,溢出表现又不同。开 MT5 把这段代码贴进脚本,逐类型试一遍比读十遍直观。

MQL5 / C++
<span class="number">class="num">01</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">02</span>. <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span>
<span class="number">class="num">03</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">04</span>. <span class="keyword">class="type">void</span> <span class="functions">OnStart</span>(<span class="keyword">class="type">void</span>)
<span class="number">class="num">05</span>. {
<span class="number">class="num">06</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(Sum(<span class="number">class="num">10</span>, <span class="number">class="num">25</span>));
<span class="number">class="num">07</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(Sum(-<span class="number">class="num">10</span>, <span class="number">class="num">25</span>.));
<span class="number">class="num">08</span>. }
<span class="number">class="num">09</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="number">class="num">10</span>. <span class="keyword">class="type">ulong</span> Sum(<span class="keyword">class="type">ulong</span> arg1, <span class="keyword">class="type">ulong</span> arg2)
<span class="number">class="num">11</span>. {
<span class="number">class="num">12</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">"::"</span>, <span class="keyword">__LINE__</span>);

模板函数如何吃掉不同类型参数

把 Sum 写成模板,编译器会在调用处按实参类型自动实例化一份对应版本,不用为 int、double、long 各写一遍。下面这段模板定义里,T 是占位类型,传入什么就编译成什么。 [CODE] template <typename T> T Sum(T arg1, T arg2) { Print(__FUNCTION__, "::", __LINE__); return arg1 + arg2; } [/CODE] 逐行看:第1行声明模板,T 为任意类型;第2行函数头用 T 收两个同类型参数;第4行先打印函数名和行号便于跟踪实例化来源;第5行返回两者之和。 实盘验证时,OnStart 里分别喂了整数 130、150 得到 280,浮点 130.0、150.0 得到 280.0,long 变量同样 280,float 变量输出 280.0。注意 uchar 场景下 v1=130、v2=150 相加为 280,但 uchar 上限 255,若换更大值会静默溢出,外汇 EA 里用模板做价格累加时得留心底层类型边界。 对比早先的非模板 double Sum(double,double),模板版在 MT5 里少写重复代码,但调试时 Print 打出的 __FUNCTION__ 会显示为 Sum<int> 或 Sum<double> 这类实例化名,帮你定位是哪一份在跑。

MQL5 / C++
class="kw">template <class="kw">typename T>
T Sum(T arg1, T arg2)
{
  Print(__FUNCTION__, "::", __LINE__);
  class="kw">return arg1 + arg2;
}

「看清模板里 typename 到底替了谁」

函数或过程模板把原本要手写多个重载的负担丢给了编译器,人只管写可读性高的代码。但真要扩展这套机制,得先弄清模板在编译期到底发生了什么,否则很容易卡在语法表面。 回看图 03,函数名后接一对尖括号,里面是类型名;而代码 03 的第 25 行没有具体类型,只写了保留字 typename 加一个标识符。这个标识符就是关键:它占位,告诉编译器此处类型待定,编译生成可执行文件时再被真实类型替换。 通常大家用 T 作这个标识符,但换成 Unknown 之类任意合法名字也行,只要定义处和使用处一致。代码 03 用了更多数据类型,反而比简单模板更容易看明白这套替换逻辑。 把第 25 行改成下面这样,编译器照样认得模板该怎么实例化。你可以直接开 MT5 建个脚本,把原 T 改成 Unknown 跑一遍,验证行为无差异。

MQL5 / C++
<span class="keyword">class="kw">template</span> &lt;<span class="keyword">class="kw">typename</span> Unknown&gt; Unknown Sum(Unknown arg1, Unknown arg2)

◍ 模板先啃透基础部分

MQL5 模板最劝退初学者的,是函数和过程的重载逻辑——本文只铺开了模板能力的第一层,但这一层不扎实,后面附带的复杂代码基本看不懂。 别急着一口吞下所有材料。模板这种东西,只有反复在 MT5 里写、改、跑,才会慢慢变成你自己的工具;附件 Anexo.zip 虽然只有 1.18 KB、示例极简,但先拆熟它们,以后读别人几千行的模板库能省掉大量时间。 知识是随时间堆出来的,不会凭空掉进脑子,也不会遗传。下一篇会回到最朴素的形式继续拆模板用法,到时候直接开 MT5 跟着敲就行。

常见问题

用函数模板代替手写重载,把类型抽成参数,编译器自动生成对应版本,省去重复劳动。
typename 是占位符,替你标「这里是个类型待定」,具体被谁替要在调用时由实参推导出来。
可以,把代码贴给小布,它会指出哪类参数会推导失败、该补哪种重载或特化。
模板按实参推导类型,指针和对象推导出的 T 不同,必要时要对指针做特化或偏特化。
模板在编译期展开,运行时无额外开销,但高频调用仍需注意高风险与过拟合。