我的 variant 解释

cxjddd 2004-05-02 10:17:25
代码在下面,不过已经发现好些 bug 了:
http://expert.csdn.net/Expert/TopicView1.asp?id=2924269

可改变真实类型的 class variant

  在我完成了 class variant 的第一版后,我想继续完成另一种不同的
variant,这也是看了强大的 boost::variant 后的打算。然而在有了一点点的
初步的想法后,发现我写不下去了,原因很简单,没的目标了。

  我的第一版的 variant(以下只说 variant)目标很明确:提供一种可以变
化在基本的几种数值类型(int、double 等)上的动态类型。具体说,就是任一
个 variant 的变量如 var,都可以参加 +、-、*、/、% 等运算,但其结果,则
依 var 的真实类型而不同。

  比如:

variant var;
var.set_type (variant_int); // 设置其真实类型为 int 类型
var = 1.5; // var == 1
var *= 2; // var == 2

var.set_type (variant_double); // 设为 double 型
var = 1.5; // var == 1.5
var /= 0.5; // var == 3.0

  variant 可以支持的类型有:int、long、float 和 double;支持与这四种
类型的变量的进行算术运算和比较运算。很不幸,variant 不能与这四种类型之
外的类型进行运算。

  一、设定类型和赋值

  在构造函数里,variant(variant_typeid) 和 variant(const string&) 可
以指定类型;而 template variant(type value) 可以由 type 的类型指定。成
员函数 set_typeid() 可以随时设定类型,用 get_typeid() 则可以得到类型的
variant_typeid 值。

  operator = 可以改变值,但不改变类型;assign() 赋值并改变类型。
template set_value(type) 把源数据当成 type 型变量再赋值,不改变类型;
相应有 template get_value() 可以取值。这些函数里,只有 assign() 是改变
类型的。

  二、算术运算

  支持的算术运算有:+、-、*、/、%,以及相应的 += 等运算符。但是注意
float 和 double 并没有 % 运算,对这个运行时错误我并没有处理(我不知道
怎样处理好)。

  这些算术运算,对需有一则是 variant 即可。但对于 += 这样的运算符,
似乎左边必须是 variant。

  三、比较运算

  比较运算支持所有的六种运算,但最终的实现是只有 == 和 < 两种,剩下
四种都可以由这两种变化而来。

  四、类型自动转换

  variant 参加各种运算,必然会遇到如 int 与 double 运算的这种情况,
这时就要类型转换了。我的设定是:int -> long -> double 和 float ->
double。可能更应该是 int -> long -> float -> double,但我对 float 实在
是有偏见,实在要打压它。


  实现:

  最开始我是用一个 union 去实现的,结果发现并不好,于是变用动态分配
内存的办法,用一个 void* 指向存储单元,而 void* 也便于转换。在 variant
里,有两个数据成员,void* _value 和 variant_typeid _type,_value 指向
真实的存储空间,而 _type 则指定了真实的类型。

  new_variant(variant_typeid type) 用来分配一个 type 类型的空间,返
回值就是空间的地址。template<variant_typeid type> new_constructor() 分
配并构造一个 type 类型的变量。new_variant() 只是用 switch 语句转而调用
new_constructor()。相似的,有 delete_variant() 和 delete_destructor(),
删除一个变量。

  这里存在有大量的 variant_typeid 到真实类型的转换,还有真实类型到
variant_typeid 的转换,这两项工作分别由 template struct typeid2type 和
template struct type2typeid 完成。对于 int、long、float 和 double 之外
的类型,这两个工具均会报错。

  template get_reference() 是用来取得 variant 真实变量的引用的,可是
我觉得它的身份很尴尬,只好在内部使用。觉得这个函数是很重要的,真正要改
变值都要靠这个。这这个函数绝不能用错了(其实哪一个都不能错啊)。

  template <variant_typeid type> set_value(value) 可以设置 variant
的值,主要是一个 switch 语句,根据 variant 真实类型的不一样而选择合适
的语句:

switch (_type)
{
case variant_int:
get_reference<variant_int>() = value;
break;
...
}

  如果真实类型是 int,那么执行时会选择 variant_int 支路,结果会是正
确的。但是,如果不是 int,也要求 variant_int 支路必须可以编译通过,则
要求 int 必须有从 type 到 int 的赋值运算符。推广开来就是,所有 variant
支持的类型,必须有相同的一系列运算符或方法,而且它们之间必须是可想互转
换的,这是 variant 最大的限制。

  对于 template get_value() 来说,需要知道返回值的类型,以正确运作。

  对于有算术运算和比较运算来说,如果运算的类型不匹配的话,则要提升到
一个较高的类型,然后再进行去算。函数 update_typeid() 就是用来计算提升
后的类型的,按照 int->long->double 和 float->double 的原则。

  算术运算和比较运算,是借助 plus_variant(right)、minus_variant()、
equal_variant()、less_variant() 等这些函数实现的,这些函数都有一个特点
就是,要求 this->_type 等级不低于 right._type。重载的那些如 + 运算符,
都是先建立一个类型等级高的临时 variant temp,然后再调用
temp.plus_variant (right) 来完成运算。这样做唯一的是为了让运算不丢失精
度,以与语法要求相似。

  对于这种 variant 来说,重要的是找准真实类型并正确实施运算。我的实
现实在是使蛮力,感叹 boost::variant 的精彩。
...全文
126 2 点赞 打赏 收藏 举报
写回复
2 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
yingle2000 2004-05-02
用ms的variant_t也不错啊,最多前面加几个转换操作符,使用也是很方便的。但是它的和上面的程序都不能进行数组操作。能进行数组操作就好了。
  • 打赏
  • 举报
回复
Wolf0403 2004-05-02
真的不知道一个只支持有限类型的(甚至只是几个原始类型的)variant 究竟有什么用处。。。效率高?体积小?有 CPU 优化?迷惑ing
  • 打赏
  • 举报
回复
相关推荐
发帖
C++ 语言
加入

6.0w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
申请成为版主
帖子事件
创建了帖子
2004-05-02 10:17
社区公告
暂无公告