缺少模板特化

在某些情况下,可能需要为特定类型(或类型集)提供与泛型不同的模板实现。例如,通常需要为指针或数组准备一个特殊版本的 swap 函数。在这种情况下,C++ 允许您执行所谓的模板特化,即定义一个模板版本,其中泛型类型参数 T 被所需的具体类型替换。

在特化函数和方法模板时,必须为所有参数指定特定类型。这称为完全特化。

对于 C++ 对象类型模板,特化可以是完全特化,也可以是偏特化:它仅指定部分参数的类型(其余参数的类型将在模板实例化时推断或指定)。可以存在多个偏特化:唯一的条件是每个特化必须描述唯一的类型组合。

遗憾的是,MQL5 中没有完全意义上的特化。

模板函数特化与重载并无二致。例如,给定以下模板 func

template<typename T>
void func(T t) { ... }

它允许以以下形式之一为给定类型(例如 string)提供其自定义实现:

// explicit specialization 
template<>
void func(string t) { ... }

或:

// normal overload 
void func(string t) { ... }

只能选择其中一种形式。否则,我们会收到编译错误“'func' - 函数已定义且具有主体”。

关于类的特化,从那些为部分模板参数指定了具体类型的模板进行继承,可以被看作是这些模板偏特化的一种等价实现。模板方法可以在派生类中被重写。

以下示例 (TemplatesExtended.mq5) 展示了将模板参数用作父类型的几种方案,包括将其中一个参数指定为特定类型的情况。

#define RTTI Print(typename(this))
   
class Base
{
public:
   Base() { RTTI; }
};
   
template<typename T
class Derived : public T
{
public:
   Derived() { RTTI; }
}; 
   
template<typename T
class Base1
{
   Derived<Tobject;
public:
   Base1() { RTTI; }
}; 
   
template<typename T>                // complete "specialization"
class Derived1 : public Base1<Base// 1 of 1 parameter is set 
{
public:
   Derived1() { RTTI; }
}; 
   
template<typename T,typename E
class Base2 : public T
{
public:
   Base2() { RTTI; }
}; 
   
template<typename T>                    // partial "specialization"
class Derived2 : public Base2<T,string// 1 of 2 parameters is set 
{
public:
   Derived2() { RTTI; }
};

我们将使用变量根据模板实例化对象:

   Derived2<Derived1<Base>> derived2;

使用 RTTI 宏进行调试类型日志记录会产生以下结果:

Base
Derived<Base>
Base1<Base>
Derived1<Base>
Base2<Derived1<Base>,string>
Derived2<Derived1<Base>>

在开发以封闭二进制形式提供的 时,必须确保针对未来的库用户预期使用的所有类型,显式实例化模板。您可以通过显式地调用函数模板,并在某些辅助函数中(例如,这些函数可以绑定到全局变量的初始化)使用具体的类型参数来创建对象,从而实现此目的。