对话#18:我为你准备一切

类别:VC语言 点击:0 评论:0 推荐:
[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。

文章来源:http://www.gotw.ca
版权归属:Herb Sutter and Jim Hyslop
译    者:徐波

对话#18:我为你准备一切[1]

    当方案出来时,我们发现它跟预想的不大一样。

  我们原先一直把注意力集中在散布于被木卫二冰层所覆盖的外星古城各处的文物里。我们在这里已花了太多的时间,埋头于地底下。我很遗憾无法在休息时间回到地面,乘坐庄严肃穆的朱庇特国王号在天空中飞翔。但现在地面上并不安全,对方部队正飞驰疾来。

    在用了雅格关于外星文字的新信息后,我们刚刚使几台设备能开动起来,虽然到目前为止我们还不知道它们干了些啥。我们知道自己必须仔细,但我们没有那么多的奢侈时间来这样做。我意识到我们的总体目标是达到领导集团所要求的获得外星技术的控制权。

    但解决方案不在外星文物里,而是在舞厅里。

    这个黑暗的,被冰层所覆盖的外星城到处都是未被勘查过的房间、深坑、走廊和死巷。在我们刚发现的东西里,最有趣的是一个巨大的空房间,半空中悬挂着三个直径十英尺左右的金属球,我们管它叫舞厅。至于这三个金属球是怎么挂上去的,我们想不出个所以然来,但当我们找到这个城市的一些能量控制设备时,它们就活动起来。

    当时我也正在场。“这是什么——”跟我们一块的一个技术专家叫道,但他马上又停住了话头,当时我们所有人所能说的也就是这样了。

    每个金属球显示一幅不同的图像。在其中一个金属球里,我们可以看到一片明亮的闪着光的微黄色的草地,不远处有一些象树木一样的东西。在另一个金属球里,我们看到的景象是黄昏时的海滩。第三个金属球显示了一个金属屋,很象这个建筑物里的一些房间,里面是成排的齐胸高的柜子。

    我们凝神细看,接下来并没有其它现象发生。我们开始围绕着这几个球转动。图像变换着,我们仿佛穿过一个洞一般透过金属球看到另一处地方,但在金属球后面,我们原先的房间仍是老样子,毫无变化。

  “那些球里有些什么?是些图片,还是……而且那些地方是在哪儿?”珍妮忍不住说道,但马上就意识到房间里只有她一个人的声音,赶紧噤声。一个技术专家正神游于其奇妙处,听了这几句话才醒过神来,赶紧去叫我们的队长,而队长,毫无疑问会叫上总管吉尔伯。

  “我不知道,”我慢声说道,怀着敬畏却又颇感好奇,“它们可以保存任何东西,可以用于各种场合……”

- - - - - - - - - - - - - - - - - - - - - - - - -

    办公室里显得比较安静。为准备渡假,许多同事已提前离开。至于我,由于到这儿还没多长时间,还没攒够休假时间,所以我不得不工作到节假日结束。当然,这使我遇到难题时,很难得到帮助,就象我刚刚遇到的这个。

    我需要一个能保存任意类型数据的变量。我的第一个直觉就是使用模板,不管怎样,在泛型程序设计方面模板总是非常出色的。

template <typename ValueType>
class multiType
{
    ValueType value_;
public:
    multiType(const ValueType &t) : value_(t) {}

    operator ValueType() { return value_; }
};
    不幸的是,我很快遇到了麻烦,在代码的某些地方,一个变量在它的生命期中可能需要保存不同类型的信息,就象这样:

{
    multiType<const char *> multiA("Hello, World");

    // …一些代码…
    multiType<long> multiB(99L);

    //…更多的代码 …
    multiA = multiB; //糟,不能从long转换到const char*。
}
    这个时候,我希望multiA忘记它是以multiType<const char*>开始其生命之旅的,并希望它如multiType<long>一般工作。看上去我有两种选择,一种是union,另一种是void指针。关于void指针的危险性,我已听得太多,所以我决定采用union:

class multiType
{
    enum ValueType { Long, ConstCharPtr };

    union
    {
        const char * c_;
        long l_;
    } value_;

    ValueType currentType_;
public:
    multiType(const char * c);
    multiType & operator =(const char * c);
    operator const char *();

    multiType(long);
    multiType & operator =(long);
    operator long();
};
    通过在类中装入一个union,我得到了某种程度的封装性。至少类型转换操作符能够检查需要保存的值类型是否适当。我并不希望在前人的基础上做些复制—粘贴工作来实现所有的迟早要用到的重载构造函数和类型转换操作符。(long和const char*只是冰山的一角。)

  “你需要any。”

    一听到Guru的声音,我从椅子上惊跳起来,跟以前我在实习期时一样。即使办公室寂静如斯,我仍没感觉到她的到来。“嗯,”我结结巴巴地说,“我需要什么?”

    “不,我的孩子,”她笑了,合上手上那本大书,“这不是个疑问句,而是陈述句。你需要any。”

    “是吗,”我回答道。我心里掂量了一番是否要采用我所擅长的车灯前的小鹿般的目光,但最终决定还是单刀直入,于是直接问道:“哦,您能讲得稍微详细点吗?”

    “一千个理由,我的徒弟,”Guru弓起身。我正怀疑在空荡如斯的办公室里是否有必要摆出十足的专家派头时,却看到鲍勃的脑袋探入厨房,来取一杯新鲜咖啡。我知道Guru摆派头主要在两种场合:一是吓唬新手,给新来者一个下马威;二是表示对鲍勃前来打扰的不满。“我没把你仅仅当作实习期的新手,”她继续说,“但作为一个程序员,你还需更多磨练。”听到这话,我不禁毛骨悚然,但接着我就意识到她没说错。我学到的东西越多,就越感到自己的无知。

    “我指的是Boost库的any类,”Guru接着说。

    “Boost库是什么?我怎么没听说过。”

    她微微一笑,但笑容中明显含有一丝诧异:“真的吗?你应该去了解。Boost库是C++源库的一个集合,它是由C++标准委员会的一些忠实信徒所创立的。

  “哦,那么说它跟标准相关喽?”

   “不是直接相关,”Guru回答说,“创立Boost的成员并非C++标准委员会的原班人马,而且Boost跟ISO以及其它任何标准组织都没有官方的联系。这些伟大的作品都在Boost的站点<www.boost.org>上,其用意是列入下一个C++神圣标准中标准库的补充项目的候选名单。当然,它们并不能保证会被标准委员会采纳。”

   “哈,”我叫了一声,表示我知道这回事了,“那您提到的any类又是怎样的呢?”

    “这个any类,简单地说,就是允许一个变量保存任意类型的值。一个any对象可以保存任何东西。例如,”她拿起笔,在我的白书写板上写道:

#include <iostream>
#include <boost/any.hpp>
const char * initialMessage = "Snowflakes keep falling on my head";
int main()
{
boost::any multiA(42L);
boost::any multiB(initialMessage);
multiA = multiB;
std::cout << any_cast<const char *>(multiA) << std::endl;
}
    “在这个例子里,multiA一开始保存一个long值,从multiB赋值后,multiA就从根本上转换为一个const char*。因此,程序的输出将是‘snowflakes keep falling on my head'。”Guru边总结边放下手中的笔。

“帅呆了。那any_cast又是干什么的?我不记得C++中有any_cast这个关键字,但它应该是个关键字,因为它没有boost::前缀……?”

    “不,它是个自由函数模板。它的家安在boost名字空间里,但由于它所传递给的那个对象同样来源于boost名字空间,编译器能够正确地执行Koenig查找法,即使没有using声明或显式的前缀修饰也能正确地找到该函数。any_cast的意思跟大红大紫的dynamic_cast差不多。它试图把给定的操作数转换为所请求的类型。如果转换可以进行,该操作返回已转换为请求类型的原对象。如果不行,若操作数是个指针,就返回一个null指针,若操作数是个引用或对象,就抛出一个异常。”

    这给了我很深印象。“好,复制any对象和取出它的值看来很容易,但在这段代码里,当创建any对象时,您已经知道您需要它保存的值。但如果当时您尚不知道这个值又将如何?您是否不得不创建另一个any帮助对象,然后再从它赋值过来呢?”

    “不,我的孩子。”不远,我发现在与卧室分隔的地方,鲍勃探出半个脑袋,微微摇动,似乎对我的话表示不同意,接着又消失了。Guru神秘地笑道:“any类提供了一个模板化的赋值操作符,允许你轻易地编写象下面这样的代码:”

{
    boost::any holdsAnything; 
    holdsAnything = 9;
    holdsAnything = std::string("Hello, world");
    holdsAnything = 3.1415926;
}
    “你还能注意到,”Guru接着说,“any类支持null或空的形式,也就是说当前的值类型未保存任何值。更进一步,为便于提取它的值,any类提供了一个type函数,它返回一个std::type_info类,提示any类当前所保存值的类型。”

    “知道了,”我插嘴道,“我又想到另外的事,我觉得这将是实现属性的好方法啊。只要创建一个struct,让它拥有一个std::string来表示属性名,一个any类来表示属性值。”

    “事实上,我年轻的徒弟,”Guru点点头,“先知Henney建议把属性作为any类的潜在用途之一[2]。因为any类可以保存任何东西,可以用于各种场合……”

   “什么?您的意思是……”

   “因为,”她解释说,“any类的另一种可能的用途是作为基于多态的回调函数的参数。如果我没记错的话,Henney给出的例子是这样的:”

class consumer
{
public:
    virtual void notify(const any &) = 0;
};
   “啊,太好了,怎样都行,”我咧嘴笑了,“感谢您提供我所需要的信息。”Guru低下头,又翻开她手上那本大书,悄然离去,而我又转头把精神集中到到键盘上。

- - - - - - - - - - - - - - - - - - - - - - - - -

    “干得真漂亮了,太漂亮了,实在是漂亮!“吉尔伯反复叫道,“不同凡响,真是不同凡响!那些文物怎么样?有什么进展吗?我们有没有找到新的能保护我们自己的武器?”

    这可把我给惹火了。“总工,”我脱口而出,“我们不清楚它是什么东西,或是它代表什么意思。我明白我们的处境,但我们所做到远不止是找件武器!我们已经执行一次测试,而且——”

  “你说话要注意自己的身份,”总管打断了我的话。这是他第一次这样发作,我不敢再吭声。接着,他又用他已经习惯了的调侃的语调说,“我们的压力都很大。我们没有太多的时间,甚至是没有时间。这个测试,是怎么样的,你们是怎么干的?”

    “用根长竿,”珍妮回答道,“我们把一根长竿插到金属球里,它进去了,没遇到阻力,接着我们可以看到在海滩上留下了一个记号。我们把长竿探得更深,超过了金属球的宽度,但长竿并没有从球的另一边伸出来,它还是里面,”她边说边打手势,想讲得更明白些。我们接过她的话头,“看上去海滩仍在老地方,在伸手可及的地方。我没试过,但我确信我能触到它。这球象是宇宙中的洞。”

    吉尔伯得到这些信息后,转移到草地那边,接着又转移到金属屋那里。

    就在此时,当我们绕金属屋围成一圈,从各个方面观察它的时候,新景象出现了。一个穿着奇特的灰袍人站在其中一个柜子边,手里拿着一个形状扁平的东西,一边操纵它。突然,好象是为了引起我们的注意,他转向我们这边,眼睛瞪着我们。他的嘴动了,经过很短暂的停顿后,我们听到一句英语:“你们应该在这儿吗?”

[参考]

[1] 调子是“I’d Do Anything,”Oliver!,1968。

[2] C++ Boost any类,Kevlin Henney, <www.boost.org/libs/any/index.html>

[关于作者]

Herb Sutter

是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过[email protected].联系他

Jim Hyslop

Leitch Technology International Inc.资深的软件设计师,你可通过[email protected]联系他

本文地址:http://com.8s8s.com/it/it2452.htm