C++是强类型语言,所有强类型语言对型别的要求都是苛刻的,型别一有不合编译器就会抱怨说不能将某某型别转换为某某型别,当然如果在型别之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非explicit构造函数创建临时变量的隐式转换或是在int,long这些基本型别间的)又另当别论。总的说来,为了保持型别安全,C++有严厉的要求。然而有时候程序员可能有这样的需要:
i;
iong j;
X x;
any anyVal=i;
...
anyVal=j;
...
anyVal=x;
...
1.它本身不能是模板类,因为如果它是模板,你必须为它的具现化提供模板参数。而事实上你并不想这样做。你想让同一个对象接受任意型别的数据。在上面的代码中这个对象是anyVal。然而,如果你必须为它提供模板参数,那么上面的代码看起来就会像这样:
any<> anyIntVal&#61;i;
any<> anyLongVal&#61;j;
...
anyIntVal&#61;i;anyLongVal&#61;j;
2&#xff0e;它必须提供某些有关它所保存的对象型别的信息。
3. 它必须提供某种方法将它保存的数值“取出来”。
事实上&#xff0c;Boost库已经提供了这样的类boost::any&#xff0c;下面我就为你讲述它的原理及构造。
首先&#xff0c;any类里面一定要提供一个模板构造函数和模板operator&#61;操作符。因为你必须允许用户写出&#xff1a;
any any_value(val);
any_value&#61;val1;
其次&#xff0c;数据的存放之所是个问题&#xff0c;显然你不能将它保存在any类中&#xff0c;那会导致any类成为模板类&#xff0c;后者是明确不被允许的。数据应该动态存放&#xff0c;即动态分配一个数据的容器来存放数据&#xff0c;而any类中则保存指向这个容器的指针&#xff0c;明确地说&#xff0c;是指向这个容器的基类的指针&#xff0c;这是因为容器本身必须为模板&#xff0c;而any类中的指针成员又必须不是泛型的(因为any不能是泛型的&#xff0c;所以any中所有数据成员都不能是泛型的)&#xff0c;所以&#xff0c;结论是&#xff1a;为容器准备一个非泛型的基类&#xff0c;而让指针指向该基类。
下面就看一看boost库是如何具体实现这两点的。
any
{
:placeholder
{
:
virtual ~placeholder()
{}
:
virtual std::type_info & type() &#61; 0;
virtual placeholder * clone() &#61; 0;
};
{
:
holder( ValueType & value)
: held(value)
{}
:
virtual std::type_info & type()
{(ValueType); }
virtual placeholder * clone()
{holder(held);
}
:
ValueType held;
};
placeholder * content;
any( ValueType & value)
: content( holder
...
any & &#61;( ValueType & rhs)
{
any(rhs).swap(*); *;
}
any & swap(any & rhs)
{
std::swap(content, rhs.content); *;
}
~any()
{content; }
...
};
“但是等等&#xff01;”&#xff0c;你急切的说&#xff1a;“你失去了型别的信息。”唔...的确&#xff0c;当赋值的模板函数返回后你也就失去了关于型别的信息。考虑下面你可能想要写出的代码&#xff1a;
i&#61;10;
boost::any anyVal&#61;i;j&#61;anyVal;
j&#61;any_cast<>(anyVal);
ValueType any_cast( any & operand)
{ValueType * result &#61; any_cast
}
ValueType * any_cast(any * operand)
{operand && operand->type() &#61;&#61; (ValueType) 1 ? &
i&#61;10;
boost::any anyVal&#61;i;d&#61;any_cast<>(anyVal);
boost::anyVal&#61;i;其实将anyVal.content指针指向了一个holder对象(请回顾上面的代码)。然后any_cast(anyVal)实际上调用了any_cast<>针对指针的重载版本&#xff0c;并将anyVal的地址传递过去&#xff0c;也就是转到1处&#xff0c;因为调用的是any_cast&#xff0c;所以1处的代码被编译器特化为
2
但是前面说过&#xff0c;operand->content实际指向的是any::holder&#xff0c;所以这个static_cast<>是“非法”的&#xff0c;然而事实是&#xff1a;它能通过编译&#xff01;原因很简单&#xff0c;holder和holder都是placeholder的基类。将基类指针向派生类指针转换被认为是合法的。但这却酿成大错&#xff0c;因为表达式2的型别将因此被推导为double&#xff01;原先holder只给int held;成员分配了sizeof(int)个字节的内存&#xff0c;而现在却要将int型的held当作double型来使用&#xff0c;也就是说使用sizeof(double)个字节内存。所以这就相当于&#xff1a;
i&#61;10;
* pd&#61;(*)(*)&i;d&#61;*pd;
使用boost::any实现virtual template成员函数
如你所知&#xff0c;C&#43;&#43;中没有提供virtual template function。然而有时候你的确会有这种需要,any可以一定程度上满足这种需要&#xff0c;例如&#xff0c;
Base
{
:
virtual Accept(boost::any anyData)
{
...
}
};Derived: Base
{:
virtual Accept(boost::any anyData)
{
...
}
};
这样的Accept函数能够接受任意类型的数据&#xff0c;并且是virtual函数