有的时候可能会需要将整数映射为类型, 看下面这个例子, 假设对于某个类, 如果它是多态的, 我们就调用其clone
方法创建一个新对象; 如果它不是多态的, 则直接调用其构造函数. 因此我们需要根据模板参数进行选择.
template <typename T, bool isPolymorphic>
class MyClass {
void foo() {
T *pObj = ...;
if (isPolymophic) {
T *pNewObj = pObj->clone();
// do something with pNewObj
} else {
T* pNewObj = new T(*pObj);
// do something with pNewObj
}
}
}
这段代码看似没有问题, 但实际隐含了一个前提条件. 要让它编译成功, 那if-else
的两个分支必须都能单独编译成功, 虽然模板参数是在编译期就确定的, 编译器完全能够在编译期知道会选择哪一个分支. 那问题就来了, 对不是多态的对象, 我们没有定义clone()
方法, 这会导致如果进入了第一个分支(虽然实际上不可能进入), 第一个分支是无法编译成功的!
那如果有办法在处理非多态对象时, 让编译器无视第一个分支就好了.
我们可以利用模板特化配合函数重载来实现这一点
template <int v>
struct int2type {
enum { value = v };
};
template <typename T, bool isPolymorphic>
class MyClass {
private:
void foo(T *pObj, int2type<true>) {
T *pNewObj = pObj->clone();
// do something with pNewObj
}
void foo(T *pObj, int2type<false>) {
T* pNewObj = new T(*pObj);
// do something with pNewObj
}
public:
void foo() {
T *pObj = ...;
foo(pObj, int2type<isPolymorphic>());
}
}
在这里我们创建了一个int2type
结构体, 根据它的模板参数不同里面的枚举值也不同. 调用公共版本的foo()
方法时, 由于isPolymorphic
是一个编译期常量, 编译器只会根据它特化出来的int2type
类型去调用正确的重载版本. 这样就利用函数重载实现了编译期选择, 最终的效果类似于让编译器"无视了"不需要的分支.
当你需要根据不同分支做不同处理, 并且分支条件在编译期就能确定, 而且必须在编译期就选择分支时, 整数映射为类型的技巧就非常有用. 总之, if-else
和int2type
的区别在于, if-else
是运行时分支选择, 要求每个分支都能单独编译成功; 而int2type
是编译期分支选择, 可以通过这个技巧让编译器选择性"无视"一些分支.