型别映射
问题:C++中不存在函数模板偏特化
有时我们期待型别转换,输入一种数据类型,输出另一种数据类型,如下代码所示。
template<typename T, typename U>
T* Create(const U& arg)
{
return new T(arg);
}
有时,系统中存在旧代码,如Widget,需要两个参数才能构建出对象,第二个参数固定为-1,它的派生类则没有这种问题。
由于函数模板特性限制,我们如下代码不符合规则。
//非法代码
template<typename U>
Widget* Create<Widget, U>(const U& arg)
(
return new Widget(arg, -1);
}
为实现上面的转化功能,我们可以利用函数的重载机制,于是,可以采用如下代码实现。
template<typename T, typename U>
T* Create(const U& arg, T//dummy)
{
return new T(arg);
}
template<typename U>
Widget* Create(const U& arg, Widget//dummy)
{
return new Widget(arg, -1);
}
此方案存在一定的风险,当第二个参数是复杂对象时,它的创建会代码很大的额外开销,并且函数根本没有使用到它。
进而,我们期待几乎为0的开销机制。
类型标签(Tag)
C++模板技术,可以实现一种类型转换成另一种类型,这种机制称为类型映射。
针对上述问题,我们可以提供一个类型映射类,如下代码。
template<typename T>
struct Type2Type
{
using originalType = T;
};
模板可以根据不同的T类型,产生不同的Type2Type<T>类型,该类型中无任何数据成员,对它的创建开销几乎可以忽略不记。
改进上面的代码,实现如下重载函数。
template<typenameT, typename U>
T* Create(const U& arg, Type2Type<T>)
{
return new T(arg);
}
template<typename U>
Widget* Create(const U& arg, Type2Type<Widget>)
{
return new Widget(arg, -1);
}
分析上述代码,可以发现Type2Type<T>只是当作一个占位符的作用,就像一个唯一性的标签。这种用法在C++标准库中随处可见,可以称之为类型标签(Tag)。