在C++中,术语“转化”(conversion)描述的是从另外一个类型的值(value)获取一个类型(type)的值的过程。可是有时候你会需要一种不同类型的转化:可能是在你有一个类型时需要获取一个值,或是其它的类似情形。在C++中做这样的转化是不寻常的,因为类型域和值域之间隔有有一堵很严格的界线。可是,在一些特定的场合,你需要跨越这两个边界,本栏就是要讨论该怎么做到这个跨越。
映射整数为类型
一个对许多的generic programming编程风格非常有帮助的暴简单的模板:
template <int v>
struct Int2Type
{
enum { value = v };
};
对传递的每一个不同的常整型值,Int2Type“产生”一个不同的类型。这是因为不同的模板的实体(instantiation)是不同的类型,所以Int2Type<0>不同于Int2Type<1>等其它的类型的。此外,产生类型的值被“存放”在枚举(enum)的成员值里面。
不管在任何时候,只要你需要快速“类型化”(typify)一个整型常数时,你都可以使用Int2Type。
问题描述:
当需要根据不同的值选择不同的行为时,普通的做法是利用if...else...或者case结构
来实现分支选择。在一个型别模板化的函数过程中,则可能出现问题。
看下面的第一部分:
Part 1
如下的范型容器NiftyContainer的元素型别范型化为T。在容器中需要实现元素的赋值行为。非常规的自定义类型,其复制行为是复杂的。 特别是具有多态特性的类型和普通的类型,其复制过程不一样。例如,普通类型可以采用逐位复制的简单方式,而具有多态特性(例如含有指针成员)的类对象则不宜采用 这种方式。此处假定普通类型的复制采用拷贝构造函数方式而多态的类型则采用一个专门的虚函数clone来做拷贝。
#include <cstdlib>
using namespace std;
class Normal {
int _i;
public:
Normal() { _i = 0; }
Normal(const Normal& nml) { _i = nml._i; }
// other members omitted
};
class Polym {
int *_pi;
Polym(const Polym&); // forbids copy-constructor
void setp(int* i) { _pi = i; }
public:
Polym() { _pi = 0; }
Polym* clone();
// other members omitted
};
template<class T, bool isPolymorphic>
class NiftyContainer
{
// other members omitted
public:
void DoSomething()
{
T* pSomeObj =new T;
if (isPolymorphic) // branch1
{
T* pNewObj = pSomeObj->clone(); // customized behavior for copying
// some operations on polymorphic object
pNewObj = 0;
}
else // branch2
{
T* pNewObj = new T(*pSomeObj); // using copy-constructor
// some operations on non-polymorphic object
delete pNewObj;
}
delete pSomeObj;
}
// other members omitted
};
int main(int argc, char* argv[])
{
// using the container with elements' type Normal
NiftyContainer<Normal, false> nc;
nc.DoSomething(); // error: 类Normal没有成员函数clone
// using the container with elements' type Polym
NiftyContainer<Polym, true> nc2; //error: 类Polym的copy-constructor private
nc2.DoSomething();
system("PAUSE");
return EXIT_SUCCESS;
}
错误解析
按照我们的预期,如果元素类型是多态的,那么只执行branch1部分,否则只执行branch2,因为两种类型的元素有不同的拷贝函数。但是编译器可不会理会这些,一律的都要加以编译,因此出错了。
对于模板来说,不同的参数意味着不同的类型。 此外,模板的编译有一个特性,即没有用到的模板函数是不会参与到编译中的,只进行语法正确性的分析。 函数可以根据参数进行重载。 3COME文档编辑
为了做到“根据常数的值(此处就是isPolymorphic)只编译其中某一个分支(例如branch1),而不编译其它分支,从而保证最后的代码中只包含需要的代码”, 我们可以利用上述的模板编译特性和函数重载特性。
template<int v>
struct Int2Type { // 创造了这个模板是为了用不同的常数值产生不同的类型
enum {value=v};
};
template<class T, bool isPolymorphic>
class NiftyContainer
{
private:
void DoSomething(T* pObj, Int2Type<true>) // function_p
{
T* pNewObj = pObj->Clone();
// some operations on polymorphic object
pNewObj = 0;
}
void DoSomething(T* pObj, Int2Type<false>) // function_n
{
T* pNewObj = new T(*pObj);
// some operations on non-polymorphic object
}
public:
void DoSomething(T* pObj)
{
DoSomething(pObj, Int2Type<isPolymorphic>());
}
// other members are omitted
};
int main(int argc, char* argv[])
{
// using the container with elements' type Normal
NiftyContainer<Normal, false> nc;// ok
// using the container with elements' type Polym
NiftyContainer<Polym, true> nc2; // ok
system("PAUSE");
return EXIT_SUCCESS;
}
在编译过程中,根据传递的isPolymorphic模板参数值的不同,编译器将选择实现哪个函数。 若为true,则实现function_p,而不会尝试去编译function_n。 这样就成功做到了编译期多态,而不是执行期多态。
这个技术的关键点就在于Int2Type模板。
3COME考试频道为您精心整理,希望对您有所帮助,更多信息在http://www.reader8.com/exam/