??xml version="1.0" encoding="utf-8" standalone="yes"?>97久久国产综合精品女不卡,久久精品国产精品亜洲毛片 ,7777精品久久久大香线蕉http://www.shnenglu.com/longshanks/zh-cnTue, 06 May 2025 23:42:51 GMTTue, 06 May 2025 23:42:51 GMT60三只猪http://www.shnenglu.com/longshanks/archive/2008/09/18/62213.htmllongshankslongshanksThu, 18 Sep 2008 11:25:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/09/18/62213.htmlhttp://www.shnenglu.com/longshanks/comments/62213.htmlhttp://www.shnenglu.com/longshanks/archive/2008/09/18/62213.html#Feedback3http://www.shnenglu.com/longshanks/comments/commentRss/62213.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/62213.html三只猪
莫华?br>

    时候听说过三只猪的故事,隐约记得故事是讲三只猪用不同方法造房子,Ҏ老狼。这些天做YӞ遇到一个无比简单的问题Q但在三U不同的语言中,却有着截然不同的解法?br>
    最q,冷不丁地接到公司下派的一个紧急Q务,做手持POS和PCE序之间交换数据的程序。各U各Lȝ中,有一个小得不L的问题,POSZ数据的字 节序和PC相反。这可不是什么啊。没错,是在太简单了。尽如此,q是引发了一Z论。做POSE序的,希望PCE序做{换。做PCE序的,希望POS E序做{换。(谁都惛_做点Q对?)Q。最l,作ؓ和事佬的我,Zl护和谐的氛_揽下了这件事。当Ӟ到底在那里做Q还是要军_的。最l选择在PC 上,毕竟PC上调试v来容易。(天煞的,q个POS机没有debugQ也没有模拟器,昄屏还没我手机的大Q做h着实费事)?br>    其实Q我的本意是惛_POS上做q个转换。因为POS用的是CQ一个不知什么年代的gccQ,可以直接操作字节。基本的代码看v来差不多应该是这P
        unsigned long InvData(unsigned long val, int n) {
            unsigned long t=val, res=0;
            for(; n >0; n--)
            {
                res = res << 8;
                res |= (unsigned char)t;
                t = t >> 8;
            }
            return res;
        }
    n是数据类型的字节长度。这里用了最长的无符h数类型。这是核心{换函敎ͼ各种cd的{换函数都可以从中zQ?br>        long InvDataLong(long val) {
            return (long)InvData((unsigned long)val, sizeof(val));
        }
        short InvDataShort(short val) {
            return (short)InvData((unsigned short)val, sizeof(val));
        }
        ...
    最后,有一个比较特D的地方Qfloat。float的编码不同于整型Q如果直接用(unsigned long)强制cd转换Q只会把float数值的整数部分赋予参数Q得不到正确的结果。正的做法Q应当是把float占用的四个字节直接映成一? unsigned longQ?br>        float InvDataFloat(float val) {
            float val=InvData(*(unsigned long*)(&val), sizeof(val));
            return *(float*)(&val);
        }
    通过float变量的地址强制转换成unsigned long*cdQ然后再dereference成unsigned longcd。当然还有其他办法,比如memcpyQ这里就不多说了。至于doublecdQؓ了简化问题,q里其忽略。如果有64位的整型Q那? double可以采用cM的解法。否则,必d专门的处理函数?br>    当然Q最l我q是使用C#写这个{换。相比之下,C#的{换代码显得更L代风呟뀂基本算法还是一P
        public static ulong DataInv(ulong val, int n)
        {
            ulong v1_ = val, v2_ = 0;

            for (; n > 0; n--)
            {
                v2_ <<= 8;
                v2_ |= (byte)v1_;
                v1_ >>= 8;
            }

            return v2_;
        }
    对于习惯于C/C++的同学们注意了,long/ulong在C#中不?字节Q而是8字节。也是C/C++中的longlong。以q个函数为基Q其它整数类型的字节序{换也有了:
        public static ulong DataInv(ulong val)
        {
            return DataInv(val, sizeof(ulong));
        }

        public static uint DataInv(uint val)
        {
            return (uint)DataInv((ulong)val, sizeof(uint));
        }

        public static int DataInv(int val)
        {
            return (int)DataInv((uint)val);
        }
        ...
    然而,面对floatQ出Cȝ。在C#中,没有指针Q无法象C那样float展开成ulong。(unsafe代码可以执行q类操作Q但q不是C#嫡亲的特性,q且是违背C#设计理念的。这里不做考虑Q。C#提供了另一U风格的操作Q?br>        public static float DataInv(float val)
        {
            float res_ = 0;

            byte[] buf_ = BitConverter.GetBytes(val);
            byte t = 0;

            t = buf_[0];
            buf_[0] = buf_[3];
            buf_[3] = t;

            t = buf_[1];
            buf_[1] = buf_[2];
            buf_[2] = t;

            res_ = BitConverter.ToSingle(buf_, 0);

            return res_;
        }
    q个做法管有些累赘Q但道理上很单:把float变量转换成一个字节流Q然后把相应的位|对调,p得了字节反序的float。相比C的float? 换,C#明显不够l。原因很单,C#Ҏ不是用来q这个的。C是一U非常底层的语言Q它的内存模型是完全直观的,与硬件系l相对应的。因而,对于q种 与机器相关的操作Q当然也昑־游刃有余。而C#定位于高层开发的高语言Q底层的内存模型是被屏蔽的,E序员无需知道和关心?br>    不过QC#的代码却拥有它的优势。只需看一D些函数的使用代码Q便不言自明了:
        //C代码
        int x=234;
        float y=789.89;
        short z=332;
        x=InvDataInt(x);
        y=InvDataFloat(y);
        z=InvDataShort(z);

        //C#代码
        int x=234;
        float y=789.89;
        short z=332;
        x=DataInv(x);
        y=DataInv(y);
        z=DataInv(z);
    在C代码中,对于不同的类型,需要用不同命名的函数。而在C#代码中,则只需使用DataInvq样一个函数名。至于届旉用那个版本的函敎ͼ~译器会 Ҏ实际的类型自动匹配。C#q用函数重蝲q个Ҏ,使得调用代码可以采用l一的Ş式。即便是数据的类型有所变化Q也无需对调用代码做M修改。(q在? 的开发过E中的得C验证Q传输数据的成员cd曄发生变化Q我也只是修改了数据l构的定义,便将问题搞定Q。这一点,在C中是无法做到的?br>    归结hQC׃侧重于底层,在数据{换方便的灉|性,使得转换代码的构建更加容易。而C#则得益于函数重蝲Q在转换代码使用斚wQ有独到的优ѝ?br>    q今为止Q三只小猪中Q还只有两只出现。下面就要第三只出场了?br>    作ؓC++的粉丝,我会自然而然地想C用C++来实现这个{换功能。于是便有了如下的代码:
       unsigned long InvData(unsigned long val, int n) {
            unsigned long t=val, res=0;
            for(; n >0; n--)
            {
                res = res << 8;
                res |= (unsigned char)t;
                t = t >> 8;
            }
        }
        long InvData(long val) {
            return (long)InvData((unsigned long)val, sizeof(val));
        }
        short InvData(short val) {
            return (short)InvData((unsigned short)val, sizeof(val));
        }
        ...
        float InvData(float val) {
            float val=InvData(*(unsigned long*)(&val), sizeof(val));
            return *(float*)(&val);
        }
    q些代码好象是C和C#代码的杂交后代。既有C的底层操作,也有C#的函数重载,兼有两者的优点?br>    不过Q还能做得更好:
        template<typename T>
        T InvData(T val) {
            T t=val, res=0;
            int n=sizeof(T);
            for(; n >0; n--)
            {
                res = res << 8;
                res |= (unsigned char)t;
                t = t >> 8;
            }
            return (T)res;
        }
    q样Q就把所有的整型都一|打了Q仅用一个函数模板,便完成了原先诸多函数所做的工作。而float版本的函数则保持不变Q作为InvData()的一个重载。按照C++的函数模?重蝲规则Qfloat版的函数重蝲被优先使用?br>
    好了Q三只小猪的故事讲完了。前两只猪各有优点Q也各有~点。而第三只猪则杂合和前两者的优点Qƈ且具有更大的q步。尽第三只猪存在各种各样的缺P但毕竟它的众多特性ؓ我们带来了很多效率和方便Q这些还是应当值得肯定的?

附:W三只小猪的其他手段Q?br>1、强制{换成字符串数l?br>template<typename T>
T InvData1(T v) {
    unsigned char* pVal1=(unsigned char*)(&v)
        , *pVal2=pVal1+sizeof(T)-1, t;
    while(pVal2-pVal1>1)
    {
        t=*pVal2;
        *pVal2=*pVal1;
        *pVal1=t;
        pVal1++;
        pVal2--;
    }
    return v;
}
2、用标准库Qblog上有人留a?br>template<typename T>
T InvData(T v) {
    unsigned char* pVal=(unsigned char*)(&v);
    size_t n=sizeof(T);
    std::reverse(pVal, pVal+n, pVal);
}
3、用traits
template<size_t n> struct SameSizeInt;
template<> struct SameSizeInt<1> { typedef unsigned char Type; };
template<> struct SameSizeInt<2> { typedef unsigned short Type; };
template<> struct SameSizeInt<4> { typedef unsigned long Type; };
template<> struct SameSizeInt<8> { typedef unsigned longlong Type; };

template<typename T>
T InvData(T v) {
    size_t n=sizeof(T);
    typedef SameSizeInt<sizeof(T)>::Type NewT;
    NewT v1=*(NewT*)(&v), v2=0;
    for(; n >0; n--)
    {
        v2= v2<< 8;
        v2|= (unsigned char)v1;
        v1 = v1 >> 8;
    }
    return *(T*)(&v2);
}

甚至可以使用tmpL其中的@环。在C++中,q类d的实现方法,完全看程序员的想象力了?)


longshanks 2008-09-18 19:25 发表评论
]]>
GP技术的展望——C--http://www.shnenglu.com/longshanks/archive/2008/08/02/57858.htmllongshankslongshanksSat, 02 Aug 2008 12:57:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/08/02/57858.htmlhttp://www.shnenglu.com/longshanks/comments/57858.htmlhttp://www.shnenglu.com/longshanks/archive/2008/08/02/57858.html#Feedback14http://www.shnenglu.com/longshanks/comments/commentRss/57858.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/57858.htmlGP技术的展望——C--
莫华?br>

    C++的复杂是公认的,管我认为在人类的聪明智慧之下,q点复杂压根儿算不上什么。不q我得承认,对于一般的应用而言QC++对程序员产生的压力还是不 的。毕竟现在有更多更合适的选择。仅仅承认复杂,q没有什么意义。我不时C生一个念_有什么办法既保留C++的优点,而消除它的缺点和复杂。我知道 D语言在做q样的事情。但是,D更多地是在就事论事地消除C++的缺P而没有在Ҏ上消除缺陷和复杂性?br id="n4.f">    一般而言Q一样东西复杂了Q基本上都是因ؓ东西太多。很昄QC++的语aҎ在众多语言中是CC的。于是,我便惛_或许把C++变成“C--”Q可以治好C++的复杂之病。在探讨q个问题之前Q让我们先从C出发Q看看C++Z如此复杂?br id="nt0i">

C和C++

    管存在q样那样的不I比如non-lalr的语法、隐式的指针cd转换{,但C语言的设计哲学却是够经典的。C语言有一个非正式的分c,认ؓ它既?a title="汇编" id="rkk6">汇编q样?a title="低语言" id="zr4u">低语言Q也?a title="Pascal" id="xrbx">Pascal那样?a title="高语言" id="ncnx">高语言Q? 而应该算作中U语aQ介于其他两c语a之间。这U分cL如其分地点出了C语言的特点和理念Q以高语言语法形式承蝲了低U语a的编E模型。低U语a的特? 是可以直接地描述gpȝ的结构。C则承了q个特点。C语言直观地反映了g的逻辑构造,比如数组是内存块Q可以等价于指针。在C语言中,我们可以几乎 直接看到g的构造,q且加以操作。这些特性对于底层开发至关重要?br id="a8ed">    然而,C的这U直观简z的模型q于底层和琐,不利于应用在那些构造复杂、变化多L应用中。针对C的这些弱点,Bjarne Stroustrup军_利用OOP技术对C语言q行攚w,从而促使了C++的诞生。C++全面Q几?00%Q地兼容CQ试图以此在不损失C语言的直? 和简z的情况下,同时具备更强的Y件工E特性,使其具备开发大型复杂系l的优势。这个目?#8220;几乎”辑ֈ了,但是代h颇ؓ可观?br id="dyop">    在经历了80?0q代的辉煌之后,C++的应用领域开始退步。一斚wQ在底层应用斚wQC++的很多特性被认ؓ是多余的。如果不使用q些Ҏ,那么 C++则同C没有什么差别。相反这些特性的使用Q对开发团队的整体能力提出了更高的要求。因而,在最底层Q很多h攑ּ了C++而回归了CQ因为那些高U特 性ƈ未带来很多帮助,反而生了很多负担。另一斚wQ在高层开发中Q业务逻辑和界面也无需那么多底层的Ҏ和苛刻的性能要求Q更多简单方ѝ上手容易的? a相比C++更加适合。C++的应用被压羃在中间层Q随着业界pȝU开发的不断专业化,C++的用规模也会越来越。(当然Q它所开发的应用往往都是? 键性的Qƈ且是没有选择余地的)。实际上QC++在这个层面也q完美的工兗目前无法取代是因ؓ没有相同U别的替代品。D或许是个强有力的竞争者,但一 斚wZ历史遗留代码的规模和应用惯性,另一斚wD也ƈ未完全解决C++面的复杂性问题,D也很隑֜可见的将来取代C++?br id="mof4">    实际上,C++的这U尴地位有着更深层次的原因。C++的本意是在保留C的底层特性基上,增加更好的Y件工E特性。但是,C++事实上ƈ未真正意义上 C留C的底层特性。回一下C的设计理念——直观而简z地反映底层g的特性。C++通过兼容C获得了这U能力。但是这里有个问题,如果我要获得C的这 U简单直观性,必L弃C++中的很多高Ҏ。这里最明显的一个例子便是podQ?a title="plain old data" id="ogl:">plain old dataQ?br id="rz7y">    在C中压Ҏ有pod的概念,因ؓ所有的对象都是pod。但是,C++中有了pod。因为C++的对象可能不是一个podQ那么我们便无法象在C当中那样 获得直观z的内存模型。对于podQ我们可以通过对象的基地址和数据成员的偏移量获得数据成员的地址Q或者反q来。但在非pod对象中,却无法这么做? 因ؓC++的标准ƈ未对非pod对象的内存布局作出定义Q因而对于不同的~译器,对象布局是不同的。而在C中,仅仅会因为底层硬件系l的差异而媄响对象布 局?br id="sbek">    q个问题通常q不显而易见。但在很多情况下为我们制造了不小的障。比如,对象的序列化Q我们试囑ְ一个对象以二进制流的Ş式保存在盘中、数据库中,? 者在|上传输Q如果是podQ则直接对象从基地址开始,按对象的大小复制出来Q或传输Q或存储Q非常方ѝ但如果是非podQ由于对象的不同部分可能? 在于不同的地方,因而无法直接复Ӟ只能通过手工加入序列化操作代码,侵入式地d对象数据。(q个问题不仅仅存在于C++Q其他语aQ如java、C# {都存在。只是它们没有很强烈的性能要求Q可以用诸如reflect{手D加以处理)。同L问题也存在于诸如hash值的计算{方面。这对很多开发工 作造成不小的媄响,不仅仅在底层Q也包括很多高层的应用?br id="tnen">    I其原因QC++仅仅试图通过机械地将C的底层特性和OOP{高层特性؜合在一P意图辑ֈ两方兼顾的目的。但是,事与愿违Q?a title="OOP" id="s_.7">OOP? 引入实际上得C的编E模型和其他更高U的抽象模型无法兼容。在使用C++的过E中Q要么只使用C的特性,而无法获得代码抽象和安全性方面的好处Q要么放 弃C的直观简z,而获得高层次的抽象能力。反而,׃C和OOP~程模型之间的矛盾,大大增加了语a的复杂性和~陷数?br id="fqu1">

舍弃

    但是Q我们可以看到在C++中,q所有的高Ҏ都与C的底层特性相冲突。很多用C而不喜欢C++的h都表C他们原意接受OBQ也是仅仅使用 。对?a title="RAII" id="typ4">RAIIQ基本上也持肯定的态度。或怹会接?a title="l承" id="ew-b">l承Q但也表露出对这U技术带来的复杂性的担心?a title="动多? id="zmfi">动多?/a>是明昑֏到排斥的技术。显然这是因为动多态破坏了C的编E模型,使得很多本来单的问题复杂化。不是它不好Q或者没用,是它打破了太多的东西?br id="hbzx">    因而,我们设想一下,如果我们去除动多态特性,那么是否会消除这c问题呢Q我们一步步看?br id="pa4y">    动多态的一个基本支撑技术是虚函?/a>。在使用虚函数的情况下,cȝ每一ơ扉K会生一?a title="虚函数表" id="i67i">虚函数表QvtableQ,其中存放的是指向虚函数的指针。这些虚函数表必d攑֜对象体中Q也是和对象的数据存放在一P臛_要关联在一P。因而,对象在内存里q不是以q箋的方式存放,而被分割成不同的部分Q甚臌n首异处(详见?a title="Inside c++ object model" id="zduz">Inside C++ Object Model》)。这侉K成了前面所说的非podȝ。一旦放弃虚函数和vtableQ对象的内存布局中,便不会有东西对象分割开。所有的对象的数据存储都是连l的Q因而都是pod。在q一点上Q通过去除vtableQ得语a回归了C的直观和单?br id="lcim">    动多态的内容当然不仅仅是一个虚函数Q另一个重要的基石是ѝ当Ӟ我们q不打算攑ּl承Q因为它q不直接破坏C的直观性和z性。不同于虚函敎ͼl承 不是完全Z动多态而生的。承最初的用途在于代码复用。当它被赋予了多态含义后Q才会成为动多态的基础。以下的代码可以有两U不同的解读Q?br id="m_a7">    class B : public A {};
    ?a title="代码复用" id="ba2m">代码复用的角度来看,Bl承自AQ表C我打算让B复用A的所有代码,q且增加其他功能。而从多态的角度来看QB是一个A的扩展,B和A之间存在is-a? 关系。(B是一个AQ。两者是站在不同层面看待同一个问题。代码复用,代表了编码的观点Q而多态,则代表了业务逻辑的观炏V但是,两者ƈ非实质上的一? 事。在很多情况下,基类往往作ؓl承cȝ某种代表Q或者接口,q在~码角度来看q没有对{的理解。而这U接口作用,则是动多态的基础。动多态通过不同的类 l承自同一个基c,使它们拥有共同的接口Q从而可以用统一的Ş式加以操作。作Z个极端,interfaceQ或者说抽象基类Q,仅仅拥有接口函数Q即vtableQ而不包含M数据成员。这是纯_的接口?br id="g88s">    然而,q里存在一个缺陗一个接口所代表的是一l类Q它成一l类同外界交互的共同界面。但是,使用基类、或者抽象基cM为接口,实质上是在用一? cd来代表一l类型。打个比方,一h凑在一起出L游,我们UC们这h?#8220;旅行?#8221;。我们知道旅行团不是一个hQ而是一个不同于“?#8221;的概c动多? 里的接口相当于把一个旅行团当作一个h来看待。尽这只是逻辑上的Q或怸个旅行团的很多行为和一个h颇ؓ怼。但是根本上而言Q两者毕竟不是相同层ơ的 概念。这L处理Ҏ往往会带来了很多弊端?br id="ddpi">    Z使承被赋予的这重作用发挥作用,q需要一w常关键的处理Q类型{换。请看以下代码:
    void func(A* a);
    B b;
    func(&b);
    最后这行代码施行了动多态,如果B override了A的虚函数的话。很昄Q如果严格地从强cd角度而言Q?amp;b是不应当作ؓfunc的实参,因ؓ两者类型不匚w。但是如果拒l接 ?amp;b作ؓ实参Q那么动多态将无法q行下去。因此,我们攑֮了类型{换的限制Q允许承类对象的引用或指针隐式地{换成基类的引用或指针。这P 形如func(&b);便可以顺理成章地成ؓ合法的代码?br id="hcgc">    然而,q也是有代h的:
    B ba[5];
    func(ba);
    后面q行函数调用实际上是一个极其危险的错误。假讑֜func()中,Ş参a作ؓ一个类型A的数l对待,那么当我们用ba作ؓ实参调用func()? 时候,会将ba作ؓA? 数组处理。我们知道,数组内部元素是紧挨着的,W二个元素的位置是第一个元素的基址加上元素的尺寸,以此cL。如果传递进来的对象数组是Bcd的,而被? 为Acd处理Q那么两者的元素位置可能不同步。尽Bl承自AQ但是B的尺寸很有可能大于AQ那么从W二个元素vQa[1]的地址qba[1]的地 址。于是,当我们以a[1]讉KbaӞ实际上很可能在ba[0]的内部某个位|读取,而func()的代码还以ؓ是在操作ba[1]。这便是C++中的 一个重要的陷阱——对象切剌Ӏ这U错误相当隐蔽,危险性极大?br id="nl9g">    ׃C++试图保留C的编E模型,因而保留了指针-数组的等h。这U等h体C数组的本质。这在C中是一利器,q无M问题。但在C++中,׃? 在了l承Q以及承类的隐式类型{换,使得q种原本滋补的特性成Z一剂毒药。换句话_C++所引入的动多态破坏了C的直观性?br id="movc">

舍弃之后

    从上面的分析来看Q动多态同C的编E模型是不相容的。因而如果希望得到C的直观性,q且消除C++的缺P必须攑ּ动多态这个特性。现在来看看攑ּ之后会怎样?br id="vfq0">    一旦放弃了动多态,也就攑ּ了虚函数和vtable。此Ӟ所有的对象都是pod了。那么首当其冲的好处Q就是可以进行非侵入的序列化、hash计算{等 操作。由于对象肯定是q箋分布的,可以直接地将对象取出q行~码、存储、计和传输Q而无需了解对象内部的数据结构和含义。另外一个重要的问题也会得到? 冻Iq就是ABI。在C中统一的ABI很自然地存在于语a中。我们可以很Ҏ地用link两个不同编译器~译的模块连接v来,而不会发生问题。但 是,C++中做不到Q除非不再用类而用纯C。目前C++q没有统一的ABIQ即便标准委员会有意建立q样的规范,实现h也绝非易事。但是,如果攑ּ 动多态之后,对象的布局便回归到C的Ş态,从而得ABI不再成ؓ一个问题?br id="v898">    另一斚wQ随着动多态的取消Q那么承的作用被仅仅局限于代码复用Q不再具有构造接口的作用。我们前面已l看刎ͼl承cd基类的隐式{换,是ؓ了基类? 够顺利地成ؓl承cȝ接口。既然放弃了动多态,那么也就无需基类再承担接口的d。那么由l承cd基类的隐式类型{换也可以被禁止:
    void func(A* a);
    B b;
    func(&b);  //~译错误Q类型不匚w
    q而对象切割也不会发生Q?br id="g8k2">    B ba[5];
    func(ba); //~译错误Q类型不匚w
    管Ҏ数组-指针的等h,ba可以被隐式地转换为B*Q但是B*不再能够隐式地{换ؓA*Q从而避免了对象的切剌Ӏ?br id="p.36">    问题是,如此单地动多态放弃掉Q就如同水和孩子一h掉那P实际上放弃了动多态带来的好处。实际上q如此。我们放弃动多态这个特性,但ƈ不打放弃它所h的功能,而是用另一U技术加以替代。这便是runtime conceptQ?a title="q里" id="rcy-">q里?a title="q里" id="s1fo">q里Q?br id="ww1_">    不同于以cd为基的interfaceQ?a title="concept" id="y8et">concept是独立于cd的系l。concept生来便是Z描述一l类型,因而是接口最理想的实现手Dc当concept runtime化之后,便具有了与动多态相同的功能Q很多方面还有所越Q?br id="cx1s">    runtime concept同样需要类似vtable的函数分z表Q但׃它不是类型,q些分派表无需存放在对象内部,可以独立攄Q可以同RTTI信息攑֜一PQ? q且只需一份。正是基于这个特性,Ҏ保证了所有对象依然是podQ依然能够保证对象布局的直观性?br id="w6r.">    同样Qruntime concept承担了接口的dQ但不象动多态那样依赖于l承和相应的隐式cd转换。(通过自动或手动的concept_mapQ。因而,我们依旧可以止Zl承关系的隐式类型{换,从而防止对象切割的情况?br id="xorp">    一旦用concept作ؓ多态的实现手段Q反倒促使原本动多态的一些麻烦得到消除。在动多态中Q必L定virtual函数。如此,在一个类中会存在? U不同Ş态的函数Q实现动多态的虚函敎ͼ和无此功能的普通函数。准地l护q样两种函数Q颇有些隑ֺ。而且Q函数是虚还是不虚,牉|到系l的设计Q必d 最初构建时定Q否则以后很难修攏V但在放弃动多态,使用concept的情况下Q只要一个承类中,使用相同的签名覆盖基cM的函敎ͼ便实C多态。当 q行concept_mapQ即接口与cȝ定时Q只会考虑l承cȝ函数Q而忽略基cM被覆盖的函数。于是,只需单的覆盖Q便实现了多态的控制。对于是 否多态一个函敎ͼx否改变基cd数的行ؓQ完全由l承cLӞ在创建基cL不必为此伤神。其l果是Q我们无需在系l设计的最初一d操心多态的问题Q? 而只需Ҏ实现的需要随时实现?br>

其他

    存在大量隐式转换也是C++常受病的一个方面,Q特别是那些PascalpȝE序员)。隐式{换的目的是带来方便,使得~码更加z,减少冗余。同时也使得一些技巧得以施行。但是,隐式转换的副作用也颇为可观。比如:
    void fun(short a);
    long a=1248;
    fun(a); //多一个警?br id="q8u5">    q种转换存在两面性:一斚wQ它可能是合理的Q因为尽acdlong大于shortQ但很可能存攄short可容U的数|但另一斚wQa的确存在short无法容纳的可能性,q便会造成一个非帔R蔽的bug?br id="nxhy1">    C/C++Ҏ的策略是把问题扔l程序员处理Q如果有bug那是E序员的问题。这也算得上合情合理Q毕竟有所得必有所失,也符合C/C++的一贯理c但 l究不是最理想的方式。但是如果象Pascal那样类型管得很死,那么语言又会失去灉|性,使得开发的复杂性增加?br id="c_mt">    如果试图止隐式cd转换Q那么ؓ了维持函C用代码的z性,函数必须Ҏ有的cd执行重蝲。这大大增加了函数实现的负担Qƈ且重复的代码严重q背?a title="DRY" id="b0w.">DRY原则?br id="bmkt">    现在或许存在一些途径Q得在l持l对强类型的情况下获得所希望的灵zL。钥匙可能依然在concept手上。考虑如下的代码:
    void fun(Integers a);
    long a=1248;
    fun(a);
    longlong b=7243218743012;
    fun(b);
    此处Qfun()是一个函敎ͼ它的形参是一个conceptQ代表了所有的整型。这Pq个函数便可以接受Q何一U整型(或者具有整型行为的cdQ。我? 怿Q在一般的应用下,M整数都有完全相同的行为。因此,我们便可以很方便C用Integersq个接口执行Ҏ数的操作Q而无需兛_到底是什么样? 整数?br id="u-s_">    如此Q我们便可以在禁止隐式类型{换,不用函数重载的情况下,完成q种函数的编写。同时可以得到更好的cd安全性?br id="g9tf">
    强制cd转换是非帔R要的Ҏ,特别是在底层开发时。但也是双刃剑,往往引来很隐蔽的错误。强制类型{换很多情况下是无理的Q通常都是软g的设计问题造成的。但l究q是有一些情况,需要它来处理?br id="xhcm">    设想q样一个场景:两个一模一LcdQ但它们分属不同的函数。(q种情Ş管不多见,但还是存在的。这往往是؜p计的l果。当然也有合理的情况Q比? 来自两个不同库的cdQ。我现在需要写一个函敎ͼ能够同时使用q两个类型。比较安全一些的Q可以用函数重蝲。但是两个重载的函数代码是一LQ典型的冗余 代码。当然也可以针对其中一个结构编写代码,然后在用时Q对另一个结构的实例执行强制cd转换。但是,强制cd转换毕竟不是件好事。因此,我们也可以构 造一个conceptQ让它描q这两个cd。然后在~写函数时用这个conceptQ当q两个类型都与conceptl定后,便可以直接用这两个c? 型,而没有类型安全和代码冗余的问题?br id="dins">     Q顺便提一下,q种方式也可以运用在cd不同的情况下。比如两个类型不完全相同Q但是基本要素都一栗那么就可以使用concept_map的适配功能Q? 两个类型统一在一个concept下。这U方式相比oop的Adapter模式Q更加简z。adapter本n是一个containerQ它所实现的接 口函敎ͼ都必M一转发到内部的对象Q编写v来相当繁琐。但在concept_map中,对于那些W合concept描述的函数无需另行? 理,concept会自动匹配,只需寚w些不W合要求的函数执行适配。)

    前面说过Q指针数l的{h性体C一U直观的~程模型。但是,指针和数l毕竟还是存在很多差别,比如指针仅仅表达了一l对象在内存中的位置Q但q未携带对象数量的信息。因而,当数l退化成指针Ӟ便已l失M数组的n份:
    void func(int* x);
    int a[20];
    func(a);
    q里Q在函数func中已l无法将a作ؓ数组处理Q因为无法知道变成int*后的a有多大来避免界。甚x们无法把a作ؓ多个对象构成的内存块看待Q因为我们不知道大小。因此,只有昑ּ地给出数l大,才能使用Q?br id="v51o">    void func(int* x, long size);
    但是Q在concept的作用下Q数l和指针得以依然保持它们的等h的情况下,解决数组退化问题。考虑q样两个函数Q?br id="ihaq">    void func1(Pointer x);
    void func2(Container x);
    其中QPointer是代表指针的conceptQ而Container则是代表容器的concept。必L意的是,Pointer是严格意义上的指 针,也就是说无法在Pointer上执行P代操作。Pointer只能作ؓ指针使用Q只具备dereference的能力(很像java?#8220;指针”Q不? 吗?concept在没有放弃C的底层特性的情况下也做到了。)。而Container则是专门用来表达容器的conceptQ其基本的特性便是P代。在 func1中,无法对Ş参x执行q代Q仅仅将其作为指向一个对象的指针处理Q保证其安全性。而对于需要进行P代操作的func2而言Qx则是可以遍历的? 于是Q对于同一个数laQ两个函数分别从不同的角度对其进行处理:
    int a[20];
    func1(a); //a直接作ؓ指针处理Q但不能q代
    func2(a); //a作ؓ容器处理Q可以P代,q且其尺怿息也一同传?br id="sq1o0">    此处实际上是利用了concept对类型特性的描述作用Q将h两重性的数组cdQ数la即代表了数组q个容器Q也代表了数l的起始地址Q以不同特征加以 表达Q以满不同应用的需求。数l仍然可以退化成指针QC的直观模型得C留,在很多特D的场合发挥作用。但在其他应用场景,可以更加安全C用数l?br id="u6hz">   

ȝ

    lg所qͼC++未能真正延箋C的直观简z,主要是由于动多态的一些基设施破坏了C的编E模型。因而,我们可以通过攑ּ动多态,及其相关的一些技术,? 之以更加“和谐”的runtime conceptQ得C++在基本保留C的编E模型的同时Q获得了相比原来更好的Y件工E特性。至此,q种改变后的C++Q如果还能称为C++的话Q拥? 如下的主q特性:
    1、SPQ来自于C?br id="anaw">    2、完全pod化?br id="anaw0">    3、OB。保留了装和RAII。尽也保留了承,但其作用仅限于代码复用,止Zl承的隐式类型{换?br id="anaw1">    4、GPQ包括static和runtime concept。这是抽象高U特性的核心和基矟?br id="f0qf3">    q样的语aҎ实质上比现有的C++更加z,但是其能力更加强大。也比C++更易于脓qC的编E模型,以便适应底层的开发。我不能说这L变化是否会生一个更好的语言Q但是我怿q些Ҏ有助于构造更加均衡统一的语a?


longshanks 2008-08-02 20:57 发表评论
]]>
GP技术的展望——先有`钧后有天 http://www.shnenglu.com/longshanks/archive/2008/07/26/57246.htmllongshankslongshanksSat, 26 Jul 2008 11:44:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/07/26/57246.htmlhttp://www.shnenglu.com/longshanks/comments/57246.htmlhttp://www.shnenglu.com/longshanks/archive/2008/07/26/57246.html#Feedback10http://www.shnenglu.com/longshanks/comments/commentRss/57246.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/57246.html

GP技术的展望——先有`钧后有天

莫华?br>

    自从高语言出现以来Q类型始l是语言的核心。几乎所有语aҎ都要以cd作ؓ先决条g。类型犹如天圎ͼ先于万物而存在。但是,是否q有什么东西比cd更加原始Q更加本质,而先于它存在呢?请往下看?)

泛型和类?/h3>     泛型最短最直观的描q恐怕应该是Qthe class of type。尽这L描述不算最完备Q但也以说明问题。早?0q代Q泛型的概念便已l出现。最初以“参数化类?#8221;的名义存在?0q代末期发展h? 恐龙U的AdaQ我的意思不是说Augusta Ada Byron Lovelace伯爵夫h是恐龙,从画像上看,q位E序员的师奉得相当漂?)Q,未拥有oopQAda83Q,便已l实C泛型QGeneric)。尽泛型历史悠久,但真正全面地发展hQ还是在90q代初, 天才?a title="Alexander a. stepanov" id="fx3k3">Alexander A. Stepanov创徏了stlQ促使了“泛型~程”Q?a title="Generic programming" id="fx3k5">Generic ProgrammingQ的立?br id="fx3k6">    Z便的目的Q我套用一个老掉牙的“通用容器”来解释泛型的概念。(q我敷衍吧:PQ毕竟重头戏在后面,具体的请看前面给出的链接Q。假设我在编E时需要一个intcd的栈Q于是我做了一个类实现q个栈:
    class IntStack {...};
    用的很好。过了两天,我又需要一个栈Q但是类型变成了double。于是,我再另写一个:
    class DoubleStack {...};
    不得了,好象是通了马蜂H,不断地出C各种cd的栈的需求,有string的,有datetime的,有point的,甚至q有一个Dialog的。每 U类型都得写一个类Q而且每次代码几乎一P只是所涉及的类型不同而已。于是,我就热切地期望出CU东西,它只是一个代码的框架Q实Cstack的所 有功能,只是把类型空着。等哪天我需要了Q把新的cd填进去,便得C个新的stackcR?br id="fx3k11">    q便是泛型?br id="fx3k12">    但是Q仅仅这些,q不以成就GP的威名?br id="fx3k13">    我有一个古怪的需求(呵呵Ql敷衍?)Q:
    做一个模板,内部有一个vector<>成员Q?br id="fx3k15">    template<typename T> A
    {
        ...
        vector<T> m_x;
    };
    可是Q如果类型实参是intcd的话Q就得用set<>。ؓ了用的方便Q模板名q得是A。于是,我们得使用下面的技巧:
    template<> A<int>
    {
        ...
        set<T> m_x;
    };
    q叫特化QspecializationQ,相当于告诉编译器如果cd实参是intQ用后面那个。否则,用前面的。特化实际上是Ҏcd实参q译器执行模板的选择。换句话_特化是一U编译期分派技术?br id="fx3k27">    q里q有另一个更古怪需求:如果cd实参是指针的话,qlist<>。这得用到另一U特化了Q?br id="fx3k28">    template<typename T> A<T*>
    {
        ...
        list<T> m_x;
    }
    q是局部特化(partial specializationQ,而前面的那种叫做昑ּ特化Qexplicit specializationQ,也叫全特化。局部特化则是根据类型实参的特征Q或者分c)执行的模杉K择?br id="fx3k34">    最后,q有一个最古怪的需求:如果cd实参拥有形如void func(int a)成员函数的类型,那么׃用deque。这?..Q有炚w。现有的C++~译器,是无法满个要求的。不q希望还是有的,在未来的新版C++09中,我们便可以解册个问题?br id="fx3k35">

Concept和类?/h3>     concept是GP发展必然l果。正如前面所提到的需求,我们有时候会需要编译器能够鉴识出类型的某些特征Q比如拥有特定的成员{等Q然后执行某U操作。下面是一个最常用的例子:
    swap()是一个非常有用的函数模板Q它可以交换两个对象的内容,q是swap手法的基。swap()的基本定义差不多是这P
    template<typename T> swap(T& lhs, T& rhs) {
        T tmp(lhs);
        lhs=rhs;
        rhs=tmp;
    }
    但是Q如果需要交换的对象是容器之cȝ大型对象Q那么这个swap()的性能会很差。因为它执行了三ơ复Ӟq往往是O(n)的。标准容器都提供了一? swap成员函数Q通过交换容器内指向数据缓冲的指针Q获得O(1)的性能。因此,swap()成员是首选用的。但是,q就需要程序员识别对象是否存在 swap成员Q然后加以调用。如果swap()函数能够自动识别对象是否存在swap成员Q那么就可以方便很多。如果有swap成员Q就调用成员Q否则, 是用上q通过中间变量交换的版本?br id="fx3k46">    q就需要用到concept技术了Q?br id="fx3k47">    template<Swappable T> void swap(T& lhs, T& rhs) {
        lhs.swap(rhs);
    }
    q里QSwappable是一个conceptQ?br id="fx3k51">    concept Swappable<typename T> {
        void T::swap(T&);
    }
    于是Q如果遇到拥有swap成员函数的对象,正好W合Swappable conceptQ编译器可以使用W二个版本,在O(1)复杂度内完成交换。否则,便用前一个版本:
    vector a, b;
    ... //初始化a和b
    swap(a,b); //使用后一个版?br id="fx3k58">    int c=10, d=23;
    swap(c, d); //使用前一个版?br id="fx3k60">    q里的swap()也是q用了特化,所不同的是在concept的指gq行的。这L特化有时也被UCconcept based overload?br id="fx3k61">    从上面的例子中可以看刎ͼ原先的特化,无论是全特化Q还是局部特化,要么特化一个类型,要么特化一个大c(如指针)的类型。但无法做到更加_。比如,? 希望一个模板能够针Ҏ有的整数QintQlongQshortQchar{)q行特化Q这在原先是无法做到的。但拥有了concept之后Q我们便可以 定义一个代表所有整数的conceptQ然后用这个整数concept执行特化。换句话_concept使得特化更加_了,整个泛型pȝ从原?#8220;? ?#8221;的变成了“q箋”的?br id="fx3k62">    不过上面那个concept特化的模板看h实在不算好看Q头上那一坨template...实在有碍观瞻。既然是concept based overloadQ那么何不直接用重载的形式Q而不必再带上累赘的template<...>Q?br id="fx3k66">    void fun(anytype a){...} //#1Qanytype是伪造的关键字,表示所有类型。这东西最好少用?br id="fx3k67">    void fun(Integers a){...} //#2QIntegers是conceptQ表C所有整型类?br id="fx3k68">    void fun(Floats a){...} //#3QFloats是conceptQ表C所有Q点类?br id="fx3k69">    void fun(long a){...} //#4
    void fun(int a){...} //#5
    void fun(double a){...} //#6
    ...
    int x=1;
    long y=10;
    short z=7;
    string s="aaa";
    float t=23.4;
    fun(x); //选择#5
    fun(y); //选择#4
    fun(z); //选择#2
    fun(s); //选择#1
    fun(t); //选择#3
    q种形式在语义上与原来的模板形式几乎一栗注意,是几乎。如下的情Ş是重载Ş式无法做到的Q?br id="fx3k84">    template<Integers T> T swap(T lhs, T rhs) {
        T temp(lhs);
        ...
    }
    q里Q模板做C两g事:其一Q模板萃取出cdTQ在函数体中Q可以用T执行一些操作,比如上述代码中的临时对象temp的构造。这个问题容易解冻I因ؓ萃取cdTq有其他的方法,一个typeof()操作W便可实玎ͼ
    Integers swap(Integers lhs, Integers rhs) {
        typeof(lhs) temp(lhs);
        ...
    }
    其二Q模板保证了lhsQrhs和返回值都是同一cd。这个问题,可以通过施加在函C的conceptU束解决Q?br id="fx3k94">    Integers swap(Integers lhs, Integers rhs)
        requires SameType<lhs, rhs>
            && SameType<lhs, retval> {  //retval是杜撰的关键字,用以表示q回?br id="fx3k97">        typeof(lhs) temp(lhs);
        ...
    }
    相比之下Q重载Ş式比较繁琐。M而言Q尽重载Ş式冗长一些,但含义更加明,更加直观。ƈ且在concept的接口功能作用下Q对参数cd一致的要求 通常q不多见Q一般在基本cdQ如整型{,的运处理中较多见。因些操作要求类型有特定的长度,以免溢出。其他类型,特别是用户定义类型,通常׃? 装的作用Q不会对cd的内部特性有q多要求Q否则就不应使用泛型法Q。如果可以改变语法的话,那么p用诸如@代替typeofQ?=代替 SameType的方法减代码量Q?br id="fx3k101">    Integers swap(Integers lhs, Integers rhs)
        requires @lhs == @rhs && @lhs == @retval {
        @lhs temp(lhs);
        ...
    }
   

Concept、类型和对象

    事情q可以有更加夸张的发展。前面对泛型q行了特化,能不能对cd也来一?#8220;特化”呢?当然可以Q?br id="z2t-">    void fun(int a);
    void fun(int a:a==0); //对于cdint而言Qa==0便是“特化”?br id="j1om">    更完整的Q也可以?#8220;局部特?#8221;Q?br id="fx3k110">    void fun(int a); //#1
    void fun(int a:a==0); //#2
    void fun(int a:a>200); //#3
    void fun(int a:a<20&&a>10); //#4
    void fun(int a:(a>70&&a<90)||(a<-10)); //#5
    ...
    int a=0, b=15, c=250, d=-50;
    fun(80); //使用#5
    fun(50); //使用#1
    fun(a); //使用#2
    fun(b); //使用#4
    fun(c); //使用#3
    fun(d); //使用#5
    实际上,q无非是在参数声明之后加上一l约束条Ӟ用以表明该版本函数的选择条g。没有约束的函数版本在没有Q何约束条件匹配的情况下被选择。对于用立 x或者静态对象的调用而言Q函数的选择在编译期执行Q编译器Ҏ条g直接调用匚w的版本。对于变量作为实参的调用而言Q则需要展开Q编译器自动生成如 下代码:
    //首先函数重新命名,赋予唯一的名U?br id="fx3k125">    void fun_1(int a); //#1
    void fun_2(int a); //#2
    void fun_3(int a); //#3
    void fun_4(int a); //#4
    void fun_5(int a); //#5
    //然后构造分zև?br id="fx3k131">    void fun_d(int a) {
        if(a==0)
            fun_2(a);
        else if(a>200)
            fun_3(a);
        ...
        else
            fun_1(a);
    }
    在某些情况下Q可能需要对一个对象的成员做出U束Q此时便可以采用q种形式Q?br id="fx3k141">    struct A
    {
        float x;
    };
    ...
    void fun(A a:a.x>39.7);
    ...
    q种施加在类型上的所?#8220;特化”实际上只是一U语法糖Q只是由~译器自动生成了分派函数而已。这个机制在Haskell{语a中早已存在,q且在用上? 来很大的灉|性。如果没有这U机Ӟ那么一旦需要增加函数分z条Ӟ那么必须手工修改分派函数。如果这些函敎ͼ包括分派函数Q是W三Ҏ供的代码Q那么修 改将是很ȝ的事。而一旦拥有了q种机制Q那么只需d一个相应的函数重蝲卛_?br id="fx3k149">    当concept-cd重蝲和类?对象重蝲混合在一hQ便体现更大的作用:
    void fun(anytype a);
    void fun(Integers a);
    void fun(Floats a);
    void fun(long a);
    void fun(int a);
    void fun(double a);
    void fun(double a:a==0.8);
    void fun(short a:a<10);
    void fun(string a:a=="abc");
    ...
    concept-cd-对象重蝲体系遵@一个原则:优先选择匚w的函C最特化的。这实际上是cd重蝲规则的扩展。大的来_所有类型比所属的 concept更加特化Q所有对象约束比所属的cd更加特化。对于concept而言Q如果concept A refine自concept BQ那么A比B更加特化。同P如果一个类型的U束Z另一个,那么前一个就比后一个更加特化,比如a==20比a>10更加特化。综合v来,可以 有这样一个抽象的规则Q两个约束(conceptQ或者施加在对象上的U束QA和BQ作用在cd或者对象上分别产生集合Q如果A产生的集合是B产生的集? 的真子集Q那么便认ؓA比B更加特化?br id="fx3k161">    Ҏq些规则Q实际上可以对一个函数的重蝲构造出一?#8220;特化?#8221;Q?br id="suy9">

    接q树的根部,泛化,接q叶子,特化。调用时使用的实参便在这?#8220;特化?#8221;上搜索,扑ֈ最匚w的函数版本?br id="fx3k163">    concept-cd-对象体系泛型、类型和对象l一在一个系l中Q得函数的重蝲Q特化)h更简单的形式和规则。ƈ且,q个体系同样可以很好地在cL板上使用Q简化模板的定义和用?br id="fx3k164">

cL?/h3>     C++的类模板特化形式q不惹h喜爱Q?br id="fx3k166">    template<typename T> A{...}; //基础模板
    template<> A<int>{...}; //昑ּ特化Q全特化Q?br id="fx3k168">    template<typename T> A<T*>{...}; //局部特?br id="fx3k169">    在C++09中,可以直接用concept定义模板的类型Ş参:
    template<Integers T> A{...};
    实质上,q种形式本n是一U局部特化,因而原本U篏赘局部特化Ş式可以废除,代之以concept风格的Ş式:
    template<Pointer T> A{...}; //Pointer表示此处采用指针特化模板
    同样Q如果推q到全特化,形式也就q一步简单了Q?br id="fx3k174">    template<int> A{...}; //q个形式有些H兀Q这里只打算表达q个意思,应该有更“和谐”的Ş?br id="fx3k175">    如果模板参数是对象,则用现有的定义形式Q?br id="fx3k176">    template<int a> A{...};
    更进一步,可以引入对象的约束:
    template<int a:a>10> A{...};
    此外QC++中在模板特化之前需要有基础模板。但实际上这是多余的QD语言已经取消了这个限Ӟq对于简化模板的使用有着莫大的帮助?br id="fx3k180">

从本质上?..

    从本质上Ԍ我们可以把所有类型看作一个集合T={ti}Q而concept则是施加在类型集合上的约束。通过conceptq个U束Q我们便可以获得c? 型集合T的一个子集C。理ZQ所有concept所对应的类型子集Cj构成了类型集合的q集{Cj}。在{Cj}中,有两cȝ型子集是很特D的。一l是 T? w,x有类型。存在一个concept不对T施加MU束Q便得到了C0=T。第二类则是另一个极端,存在一lconceptQ施加在T上之后所得的c? 型子集仅包含一个类型:Ci={ti}。由于这lconcept与类型存在一一对应的关p,那么我们便可以用q组concept来指代类型。也是把类? 作ؓҎ的concept处理。如此,concept便同cdl一在一个体pM。这U处理可以我们获得极大的好处?br id="fx3k63">     q组Ҏ的concept仍旧使用对应的类型名作ؓU谓Q仍旧称之ؓ“cd”Q但其本质上q是concept。Q何一个类型,一旦创建,也就创徏了相应的Ҏconcept。如果在模板特化中用一个类型的时候,实际上就是在使用相对应的那个ҎconceptQ?br id="fx3k64">     void func(typeA a); //管使用了类型名typeAQ但实际上这里所指的是typeA所对应的那个特Dconcept?br id="fx3k65">     在这个concept体系的作用下Q函数模板的特化和重载整个地l一hQconcept based overloadQ?br id="s8-t">    至于作用在类型上的U?#8220;特化”Q也是同L道理。对于一个类型T而言Q它所有的对象构成一个集合O。如果存在一l约束作用于OQ那么每 一个约束对应着O的一个子集。理ZQ我们可以构造出一l约束,使得他们同O的每一个子集一一对应。同Pq些子集中有两类子集比较Ҏ。一cL所有对? 的集合。另一cM是只有一个对象的子集。于是,我们可以使用q组Ҏ对象子集所对应的约束指代相应的对象。也是对象看作特D的U束。如此,cd和对? 也被l一在一个系l中了?br id="hh4e">    q而,cd在逻辑上被作ؓҎconcept处理Q对象则被作为特D的cd处理。于是,q三者便可以l一在一个体pMQ一同参与特化?br id="fx3k181">

ȝ

    管形式不能代表本质Q但形式的变化往往会带来很多有益的q步。更重要的是Q很多本质上的变化M伴随着形式上的改变。通过concept、类型和对象 在逻辑上整合到l一的体pM中,便可以促使模ѝ特化、函数重载等机制在Ş式上达成l一。从而能够简化这些功能的使用。这也是当前重视语言Q工P易用? 的潮的一个必然诉求。这个Ş式上的统一q语法p之cȝ表面变化。而是完全依赖于conceptq个新型的类型描qͼ泛型Q系l的立和发展? concept的出玎ͼ弥补了以往泛型的不I扑֛了泛型系l缺q环节QI补了泛型同类型之间的裂痕。在此作用下Q便可以构徏起concept-cd- 对象的抽象体p,用统一的系l囊括这三个原本分立的概c在q个新的三位一体的pȝ下,使得模板的特化和重蝲拥有了相同的形式Q进而获得更直观的语义,? 更好的易用性?img src ="http://www.shnenglu.com/longshanks/aggbug/57246.html" width = "1" height = "1" />

longshanks 2008-07-26 19:44 发表评论
]]>
C++的营兠Z—swap手法http://www.shnenglu.com/longshanks/archive/2008/02/26/43258.htmllongshankslongshanksTue, 26 Feb 2008 07:16:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/02/26/43258.htmlhttp://www.shnenglu.com/longshanks/comments/43258.htmlhttp://www.shnenglu.com/longshanks/archive/2008/02/26/43258.html#Feedback3http://www.shnenglu.com/longshanks/comments/commentRss/43258.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/43258.html阅读全文

longshanks 2008-02-26 15:16 发表评论
]]>
C++的营?/title><link>http://www.shnenglu.com/longshanks/archive/2008/02/16/42791.html</link><dc:creator>longshanks</dc:creator><author>longshanks</author><pubDate>Sat, 16 Feb 2008 00:19:00 GMT</pubDate><guid>http://www.shnenglu.com/longshanks/archive/2008/02/16/42791.html</guid><wfw:comment>http://www.shnenglu.com/longshanks/comments/42791.html</wfw:comment><comments>http://www.shnenglu.com/longshanks/archive/2008/02/16/42791.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.shnenglu.com/longshanks/comments/commentRss/42791.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/longshanks/services/trackbacks/42791.html</trackback:ping><description><![CDATA[     摘要: C++的营?莫华?    动物都会摄取食物Q吸收其中的营养Q用于自w生长和zd。然而,q食物中所有的物质都能为动物所吸收。那些无法消化的物质Q通过消化道的另一_某些?物消化道只有一_排出体外。不q,一U动物无法消化的排泄物,是另一U动物(生物Q的食物Q后者可以从中摄取所需的营充R?nbsp;   一门编E语aQ对于程?..  <a href='http://www.shnenglu.com/longshanks/archive/2008/02/16/42791.html'>阅读全文</a><img src ="http://www.shnenglu.com/longshanks/aggbug/42791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/longshanks/" target="_blank">longshanks</a> 2008-02-16 08:19 <a href="http://www.shnenglu.com/longshanks/archive/2008/02/16/42791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>瓦格U的排场http://www.shnenglu.com/longshanks/archive/2008/02/14/42718.htmllongshankslongshanksThu, 14 Feb 2008 03:25:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/02/14/42718.htmlhttp://www.shnenglu.com/longshanks/comments/42718.htmlhttp://www.shnenglu.com/longshanks/archive/2008/02/14/42718.html#Feedback1http://www.shnenglu.com/longshanks/comments/commentRss/42718.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/42718.html瓦格U的排场     q个春节q的实在无趣。走完亲戚,招待完亲戚,逛街买好东西Q就没多时间了。看书的兴致也没了。想写点什么,L没法集中_֊。实在腻味了Q把以前下蝲的瓦格纳的歌剧《尼伯龙Ҏ环》拿出来看看。自从下载,没怎么好好看过Q这回算是补上了?br>     瓦格U的“指环”pd可以是歌剧里的极品Qd四出Q莱茵黄金、女武神、齐g雷d和众的黄昏。分成四个晚上连演,d加v来大U?5个小时。不说别 的,里面的角色众多,光神有8个,人有7个,x?个,g龙根矮h2个,q有三个仙女?个巨人,和一只小鸟(在后台的女高韻I。情节错l复杂,? 乐更是宏大。资料上_q部歌剧中有200多个动机l合、交l在一赗不q,q还不能表现出瓦格纳在音响上q乎变态的q求。指环系列要求一个超q?00? 庞大的乐团,q且引入了几U新的乐器,包括 Wagner tuba, bass trumpet和contrabass trombone。其中, Wagner tubaq是他ؓq部歌剧专门发明的。最夸张的是Q瓦格纳Z获得如此庞大的乐队同演员声音之间的^衡,专门建造了一座歌剧院Q也是著名?font size="-1">拜罗伊特节日剧院(Bayreuth Festspielhaus)。这在音乐史上是l无仅有的。正是由于这U骇人的排场Q造就了歌剧史上的巅峰之作?br>     q倒让我联惛_C++。说实在的,C++的用,有时也同瓦格U写的歌剧那样Q非常复杂、庞大,需要大量的投入Q和前期准备。有ӞZ一些应用,而去构造一些的基础设施Q就?/font>拜罗伊特节日剧院Q。这U庞大导致了应用面的狭窄Q但是却能够获得极品般的东西。这U东西当然不会是到处都有Q但却是强大的、伟大的Q以及关键性的?br>     当然Q排Z仅是表面的东西,真正吸引人的Q还是瓦格纳的音乐。歌剧在瓦格Ux里,不再是一pd的咏叹调。瓦格纳是在用音乐讲故事。音乐是歌剧的一部分Q? 歌唱是音乐的一部分Q布景、灯光和舞台效果都是不可分割的一分子。所有这些是一个整体,除了个别出彩的乐D(基本上只?#8220;飞驰的女武神”q一D,曾被用在 电媄“C启示?#8221;中)Q很单独演奏。它们戏剧性太ZQ脱M歌剧Q就仅仅是一堆音W而已?br>     q一点上QC++也是如此Q语a、库、惯用法{等Q都是整体,一旦相互脱,便无法发挥应有的作用。所以孤立地q用C++某一斚w的特性,往往会误入歧途,只有l合q用各种手段Q才能真正地用好C++?br>     瓦格U_大复杂和莫扎特的z优雅Ş成了鲜明的对比。但是我们不能说莫扎Ҏ瓦格Ux好,或者反q来。他们的音乐都是最伟大的杰作,一个h可以毫无冲突? 同时成ؓ他们俩h的粉丝(像?)Q。我们总能从中获得惛_、思考、思想和u理,两者都具备无法替代的营充R认真地学习和吸Ӟ才是正道?br>     ~程语言的学习和使用Q又何尝不是如此呢?

注:严格地说Q瓦格纳的这些作品ƈ不是歌剧Q有一个正式的名称Qmusic dramaQ直接翻译是“音乐戏剧”Q是歌剧的扩展。但是ؓ了方便,q是q义上地其UC歌剧?br>


longshanks 2008-02-14 11:25 发表评论
]]>
当GPL遇上MPhttp://www.shnenglu.com/longshanks/archive/2008/01/25/41892.htmllongshankslongshanksFri, 25 Jan 2008 07:09:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/01/25/41892.htmlhttp://www.shnenglu.com/longshanks/comments/41892.htmlhttp://www.shnenglu.com/longshanks/archive/2008/01/25/41892.html#Feedback4http://www.shnenglu.com/longshanks/comments/commentRss/41892.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/41892.html当GPL遇上MP

莫华?/p>

    GPLQ也是General Purpose LanguageQ是我们使用的最多的一c语a。传l上QGPL的语法,或者特性,是固态的。然而,E序员都是聪明hQ即便算不上“最聪明”Q也得?“很聪?#8221;?)Q,往往不愿受到语法的束~,试图按自q心意“攚w?#8221;语言。实际上Q即便是早期的语aQ也提供了一些工P供聪明h们玩弄语法。我看的W一本C语言的书里,有q么一个例子,展示U?#8220;邪恶”的手D:
      #define procedure void
      #define begin {
      #define end }
    然后Q?br>      procedure fun(int x)
      begin
          ...
      end
    实际上,它的意思是Q?br>      void fun(int x)
      {
          ...
      }
    q可以看作是对初学C语言的PascalE序员的安慰。这U蹩脚的戏法可以作元编E的一U,在一U语a里创造了另一个语法。不q,实在有些无聊。然而,在实际开发中Q我们或多或地会需要一些超法范围的机制。有时ؓ了完善语aQI补一些缺憾;有时Z增强一些功能;有时Z获得一些方ѝ更新潮的,是试囑֜一UGPL里构建Domain Specific LanguageQ或者说“子语a”Q以获得某个Ҏ领域上更直观、更z的~程方式。这些对语言的扩展需求的实现Q依赖于被称为Meta- ProgrammingQMPQ的技术?br>    另一斚wQ随着语言功能和特性的不断增加Q越来越多的人开始抱怨语a太复杂。一斚wQ?#8220;N我们会需要那些一辈子也用不到几回的语a机制Q来增加语言的复杂性和学习使用者的负担吗?”。另一斚wQ?#8220;有备无患Q一个语a机制要到q在眉睫的时候才去考虑吗?”。但MP技术则这对矛盾消弭于无Ş。一U语aQ可以简z到只需最基本的一些特性。而其他特定的语言功能需求,可以通过MP加以扩展。如果不需要某U特性,那么只要不加载相应的MP代码卛_Q而无需为那些机制而烦恹{?br>    MP最׃h的地方,莫过于我们可以通过~写一个代码库便得语a具备以往没有的特性?br>    然而,全面的MP能力往往带来巨大的副作用Q以至于我们无法知道到底是好处更多,q是副作用更多。语a的随意扩展往往带来某些危险Q比如语法的冲突和不兼容Q对基础语言的干扎ͼ关键字的泛滥{等。换句话_MP是孙悟空Q本领高强。但没有紧箍咒,是管不住他的?br>    那么Q紧咒是什么呢Q这是q里打算探讨的主题。本文打通过观察两种已存在的MP技术,分析它们的特点与~陷Q从中找决问题的Q可能)途径?

AST?/h4>

    首先Q先来看一下宏Q这U远古时代遗留下来的技术。以及它的后裔,ast宏?br>    关于传统的宏的MP功能Q上面的代码已经单地展示了。但是,q种功能是极其有限的。宏是通过文本替换的Ş式,把语a中的一些符受操作符、关键字{等替换成另一UŞ式。而对于复杂的语法构造的创徏无能为力。问题的另一面,宏带来了很多副作用。由于宏的基是文本替换,所以几乎不受语法和语义的约束。而且Q宏的调试困难,通常也不受命名空间的U束。它带来的麻烦,往往多于带来的好处?br>    ast宏作Zl宏的后l者,做了改进Q得宏可以在astQAbstract Syntax TreeQ结构上执行语法的匹配。(q里需要感?a >TopLanguage上的Olerev兄,他用z而又清晰的文字,Ҏq行了ast宏的初培训:)Q。这P便可以创造新的语法:
      syntax(x, "<->", y, ";")
      {
          std::swap(x, y);
      }
    当遇C码:
      x <-> y;
    的时候,~译器用std::swap(x,y);加以替换。实际上Q这是将一U语法结构映到另一个语法结构上。而ast宏则是这U映的执行者?br>    但是Qast宏ƈ未消除宏本n的那些缺陗如果x或者y本n不符合swap的要求(cd相同Qƈ且能复制构造和赋|或者拥有swap成员函数Q,那么 ast宏调用的时候无法对此作出检验。宏通常以预~译器处理,ast宏则其推迟到语法分析之时。但是此时依然无法得到x或y的语义特征,无法直接在调用点l出错误信息?br>    同时Qast宏还是无法处理二义性的语法构造。如果一个ast宏所定义的语法构造与主语aQ或者其他ast宏的相同Q则会引发؜乱。但是,如果单粗暴地这U?#8220;重定?#8221;作ؓ非法处理Q那么会大大~小ast宏(以及MPQ的应用范围。实际上Q这U语法构造的重定义有其现实意义,可以看作一U语法构造的“重蝲”Q或者函敎ͼ操作W)重蝲的一U扩展?br>    解决的方法ƈ不复杂,是为ast宏加上约?/strong>。实际上Q类似的情Ş在C++98的模板上也存在,而C++则试N过为模板添加conceptU束加以解决。这U约束有两个作用Q其一Q在W一旉对ast宏的使用q行正确性检验,而无需{到代码展开之后Q其二,用以区分同一个语法构造的不同版本?br>    于是Q对于上qC子可以这h加约束(q些代码只能表达一个意思,q无法看作真正意义上的MP语法Q:
      syntax(x, "<->", y, ";")
           where x,y is object of concept (has_swap_mem or (CopyConstructable and Assignable))
                && typeof(x)==typeof(y)
      {
          std::swap(x,y);
      }
    如此Q除非xQy都是对象Qƈ且符合所指定的conceptQ否则编译器会当卛_以拒l,而且直截了当?br>    不过Q如此变化之后,ast宏将不会再是宏了。因U约束是语义上的Q必ȝ到语义分析阶D,方能验。这pZ宏的领地了。不q既然ast宏可以从预处理阶D|q到语法分析阶段Q那么再推迟一下也没关pR再_我们x的是q种功能Q带U束的ast宏到底是不是宏,也无关紧要?/p>

TMP

    下面Q我们回q头Q再来看看另一UMP技术——TMPQ参考David Abrahams, Aleksey Gurtovoy所著的《C++ Template Metaprogramming》)。对于TMP存在颇多争议Q支持者认为它提供了更多的功能和灵zL;反对者认为TMPq于trickyQ难于运用和调试。不怎么PTMP的出现向我们展示了一U可能性,卛_GPL中安全地q行MP~程的可能性。由于TMP所q用的都是C++本n的语a机制Q而这些机刉是相容的。所以,TMP所构徏?MP体系不会同GPL和其他子语言的语法机制相冲突?br>    实际上,TMP依赖于C++的模板及其特化机制所构徏的编译期计算体系Q以及操作符的重载和模板化。下面的代码摘自boost::spirit的文:
      group = '(' >> expr >> ')';

      expr1 = integer | group;

      expr2 = expr1 >> *(('*' >> expr1) | ('/' >> expr1));

   expr = expr2 >> *(('+' >> expr2) | ('-' >> expr2));

    q里表达了一lEBNFQ语法着实古怪,q咱待会儿再_Q?gt;>代表了标准EBNF?#8220;followed by”Q?代表了标准EBNF?Q从双Ud左边Q,括号q是括号Q|依旧表示Union。通过对这些操作符的重载,赋予了它们新的语义(即EBNF的相兌义)。然后配合模板的cd推导、特化等{机Ӟ变戏法般地构造出一个语法解析器Q而且是编译时完成的?

    管在spirit中,>>?、|{操作符被挪作他用,但是我们依然可以在这些代码的前后左右插入诸如Qcin>> *ptrX;的代码,而不会引发Q何问题。这是因?gt;>{操作符是按照不同的cd重蝲的,对于不同cd的对象的调用Q会调用不同版本的操作符重蝲Q互不干扎ͼ老少无欺?

    但是QTMP存在两个问题。其一Q错误处理不뀂如果我不小心把W二行代码错写成Qexpr1 = i | group;Q而i是一个intcd的变量,那么~译器往往会给Z些稀奇古怪的错误。无非就是说cd不匹配之cȝQ但是很晦ӆ。这斚w也是TMP受h诟病的一个主要原因。好在C++0x中的concept可以Ҏ板作出约束,q且在调用点直接l出错误提示。随着q些技术的引入Q这斚w问题会得到~解?

    其二Q受到C++语法体系的约束,MP无法自由地按我们习惯的Ş式定义语法构造。前面说q了Qspirit的EBNF语法与标准EBNF有不的差异Q这对于spirit的用造成了不ѝ同P如果试图q用TMP在C++中构造更高的DSL应用Q诸如一U用于记账的帐务处理语言Q将会遇到更大的限制。实际上TMP下的DSL也很有令h满意的?

    所以说QTMP在用上的安全性来源于操作W复用(或重载)的特性。但是,操作W本w的语法Ҏ是固定的,q得依赖于操作W(泛化或非泛化Q重载的TMP不可能成为真正意义上的MP手段?

    那么Q对于TMP而言Q我们感兴趣的是它的安全性和相容性。而对其不满的Q则是语法构造的灉|性。本着“dp粕Q取其精?#8221;的宗旨,我们可以对TMP做一番改q,以获得更完整的MP技术。TMP的核心自然是模板Q类模板和函?操作W模板)Q在concept的帮助下Q模板可以获得最好的安全性和相容性。以此ؓ基础Q如果我们将模板扩展到语法构造上Q那么便可以在保留TMP的安全性和相容性的情况下,获得更大的语法灵zL。也是_我们需要增加一U模李쀔?strong>语法模板Q?

      template<typename T>
      syntax synSwap=x "<->" y ";"
         require SameType<decltype(x), T> && SameType<decltype(y), T>
                && (has_swap_mem<T> || (CopyConstructable<T> and Assignable<T>)
      {
          std::swap(x, y);
      }

    q里我杜C关键字syntaxQƈ且允ؓ语法构造命名,便于使用。而语法构造描qͼ则是{号后面的部分。require沿用自C++0x的concept提案。稍作了些改造,允许concept之间的||?

用户定义的语?/strong>

    如果比较带约束的ast宏和语法模板Q会发现极其怼。实际上Q两者殊途同归,展示了几乎完全相同的东西。ast宏和TMP分别位于同一个问题的两端Q当它们的缺陷得到I补时Q便会相互靠拢,最l归l到一UŞ式上?nbsp;

    现在Q通过l合两种Ҏ的特Ԍ我们便可以得C个最l的MP构造,既安全,又灵z:

      syntax synSwap=x "<->" y ";";

      where

          (x,y is object)

          && SameType<x, y>

          && (has_swap_mem<x> || (CopyConstructable<x> and Assignable<x>))

      {

          std::swap(x, y);

      }

    我去掉了template关键字,在约?where)的作用下Qtemplate和类型参数列表都已经没有必要了。同Ӟ也允许直接将对象攑օ conceptQAssignable<x>Q这相当于:Assignable<decltype<x>>?

    “is ...”是特别的U束Q用来描q那些超concept范畴的特性?#8220;...”可以是关键字“object”Q表明相应的标识W是对象Q也可以是关键字 “type”Q表明相应的标识W是cdQ或者是关键?#8220;concept”Q指定相应的标识W是concept{等。操作符的重载所涉及的参数只会是对象Q只需对其cd做出U束Q因此concept便够用。但语法构造的定义则广大的多,它不仅仅会涉及对象,更可能涉及其它类型的语法要素。实际上Q语法构造所面对的参数是“标识W?#8221;。那么一个标识符上可能具备的各种Ҏ,都应当作为约束的内容。这里大致归U_以下几点需要约束的Ҏ:

  1. 分类。对象、类型、concept、函数等{。is ...Q?
  2. 来源。来自哪个namespace。from ...Q?
  3. 寸。类型大。sizeof(x)>20Q?
  4. 从属。指定一个语法构造是否从属于其他语法构造(其他语法构造的一部分Q。如果是从属语法构造,那么不能单独出现在独立的语句中。更q一步,可以指定一个语法构造从属于某个语法构造,不允许在其他语法构造中使用。belong_to ...Q?
  5. cd及对象间的关pR诸如ѝ子对象、子cd、true typedef、别名、成员等{?
  6. ...

    也可以认为,语法构造的U束是concept的自然g伸。concept对类型做出约束,而语法构造的U束的对象,则是标识W?

    Z强化语法构造的创徏能力Q应当允许在语法构造的定义中用BNFQ或其他cM的语法描q语a?

    在语法构造约束的支援下,便可以化解一些子语言同宿主语a之间的语法冲H。比如,我们创徏了一U类似spirit的EBNF子语aQ可以按照标准的EBNF形式~写语法Q构造一个语法解析器Q?

      syntax repeat1=p '+'

      where

          p is object of type(Parser)

      {...}

    q样便定义了一个后~形式?操作W,但仅仅作用于cdParser的对象上。对于如下的代码Q?

      letter + number

    使用主语a?Q加Q,q是使用EBNF?Q重复,臛_一ơ)Q取决于letter和number的特征(q里是类型)Q?

      int letter, number;

      letter + number; //使用主语a?Q表Cletter加number

      Parser letter, number;

      letter + number; //使用EBNF?Q表CZ串letter后面跟一个number

    如此Q便使得用户定义的语法不会同主语a的相同语法发生冲H?

    但是Q语法构造的U束q不能完全消除所有的语法冲突。其中一U情况便是常量的使用。比如:

      'x' + 'y'

    它在宿主语言中,会被识别Z个字W的相加。但在BNF子语a中,则会是若q个字符'x'之后紧跟一?#8216;y'。由于没有类型、对象等代码实体的参与,~译器无法分辨用哪U语a的语法规则来处理。解决的Ҏ是得常量类型化。这U做法在C++{语a中已l运用多q。通常采用为常量加前后~Q?

      'x'b_ + 'y'b_

    以此代表BNF子语a的常量。常量的语法定义可以是:

      syntax bnf_const="'" letter "'" "b_";

      where

          is const of ... //...可以是某U类?

      {...}

    通过is const ofU束Q将所定义的常量与某个cdl定。而通过cdQ编译器便可以推断出应当使用那种语法对其处理?

    然而,管带约束的语法构造定义可以在很大E度上消除语法冲H和歧义Q但不可能消除所有的语法矛盾。另一斚wQ结构相|但语义完全不同的语法构造؜合在一P即便不引发矛盾,也会对用者造成难以预计的؜乱。因此,在实际环境下Q需要通过某种“语法围栏”严格限定某种用户定义语法的作用范围。最直接的Ş式,是通过namespace实现。namespace在隔d协调命名冲突中v到很好的作用Q可以进一步将其运用到MP中。由于namespace一l打开Q其作用范围不会出最内层的代码块作用域:

      {

          using namespace XXX;

          ...

      } //XXX的作用范围不会超个范?

    q用q个Ҏ,可以很自然地蕴藏在一个namespace中的用户定义语法构造限制在一个确定的范围内?

    但是Q仅此不够。毕竟语法不同于命名Q不仅会存在冲突Q还会存在(合法的)hQ而且往往是非常危险的。ؓ此,需要允许在using namespace的时候进一步指定语法的排他性。比如:

      {

          using namespace XXX exclusive;

          ...

      }

    如果namespace中的用户定义语法与外部语法(宿主语言Q或外围引用的namespaceQ重叠(冲突或؜淆)Q外部语法将被屏蔽。更q一步,可以允许不同U别的排他:排斥重叠和完全屏蔽。前者只屏蔽重叠的语法,q种U别通常用于扩展性的用户语法构造(扩展主语aQ需要同主语a协同工作。而且语法重叠较少Q;而后者则外部语法统l屏蔽,只保留所打开的namespace中的语法Q子语言在代码块中是唯一语言Q,q种U别用于相对独立的功能性子语言Q可以独立工作的子语aQ通常会与主语a和其他子语言间存在较大的语法重叠Q比如上qBNF子语a{等Q?

    为提供更多的灉|性,可以在完全屏蔽的情况下,通过特定语句引入某种不会引发冲突的外部语法。比如:

      {

          using namespace XXX exclusive full;

          using host_lang::syntax(...); //引入主语a的某个语法,...代表语法名称

          ...

      }

    通常情况下,子语a不会完全独立q作Q需要同宿主语言或其他子语言交互Q也是数据交换。主语言代码可能需要将数据对象传递给子语aQ处理完成后再取回。由于编译器依赖于标识符的特性(主要是类型)来选择语法构造。所以,一U子语言必须使用其内部定义的cd和对象。ؓ了能够交换数据,必须能够把主语言的对象包装成子语a的内部类型。通过在子语言的namespace中进行true typedefQ可以将主语acd定义成一个新的(真)cdQ在q入子语a作用域的时候,做显式的cd转换。另外,Z方便数据转换Q还可以采用两种ҎQ将主语acdmapC个子语言cdQ相当于l予“双重国籍”Q,q入子语a的语法范围时Q相应的对象在类型上h两重性,可以被自动识别成所需的类型,但必d保不发生语法؜淆的情况下用;另一U稳妥些的方法,可以允许执行一UtypedefQ介于alias和true typedef之间Q它定义了一个新cdQ但可以隐式地同原来的类型相互{换。数据交换的问题较复杂,很多问题未理清Q有待进一步考察?

ȝ

    作ؓMP手段Qast宏拥有灵zL,而TMP则具备安全性,两者各自的优点相结合,使我们可以获得更加灵zd安全的语法构造定义手Dc通过在用户定义的语法构造上施加全面的约束,可以很好地规避语法冲H和歧义?

    但是Q需要说明的是,q里所考察的仅仅局限在用户定义语法构造的冲突和歧义的消除上。GPL/MP要真正达到实用阶D,q需要面Ҏ多问题。比如,׃存在用户定义的语法构造,语法分析阶段所面对的语法不是固态的Q需要随旉地接受新语法Q甚至重叠的语法Q存在多个候选语法,不同的候选语法又会生不同的下语法Q,q就使语法分析大大复杂;语法模式匚w被推q到语义分析阶段Q此前将无法Ҏ些语法错误作出检验;一个语法构造的语义需要通过宿主语言定义Q如何衔接定义代码和周边的环境和状态;如何为用户定义的语法构造设|出错信息;׃某些语法构造的二义性,如何判别语法错误属于哪个语法构造;...。此外还有一些更本质性的问题Q诸如语法构造的重蝲和二义性是否会更容易诱使用者生更多的错误{等Q牵涉到错综复杂的问题,需要更多的分析和试验?

    另外Q作为MP的重要组成部分,~译期计能力也臛_重要。TMPq用了C++模板特化QD语言通过更易于理解的static_if{机Ӟ都试图获得编译期的计能力,q些机制在完整的MP中需要进一步扩展,而ƈ非仅仅局限在与类型相关的计算上。其他一些与此相关的Ҏ,包括反射Q编译期和运行期Q、类型traits{,也应作ؓMP必不可少的特性?



longshanks 2008-01-25 15:09 发表评论
]]>
GP技术的展望&mdash;&mdash;道生一Q一生二http://www.shnenglu.com/longshanks/archive/2008/01/06/40571.htmllongshankslongshanksSun, 06 Jan 2008 09:17:00 GMThttp://www.shnenglu.com/longshanks/archive/2008/01/06/40571.htmlhttp://www.shnenglu.com/longshanks/comments/40571.htmlhttp://www.shnenglu.com/longshanks/archive/2008/01/06/40571.html#Feedback7http://www.shnenglu.com/longshanks/comments/commentRss/40571.htmlhttp://www.shnenglu.com/longshanks/services/trackbacks/40571.html

GP技术的展望——道生一Q一生二

by  莫华?span style="font-size: 8pt;">



    长期以来Q我们始l把GPQ泛型编E)作ؓ一U辅助技术,用于化代码结构、提高开发效率。从某种E度上来Ԍq种观念是对的。因今ؓ止,GP技术还只是一U编译期技术。只能在~译期发挥作用,一旦Y件完成编译,成ؓ可执行代码,便失M利用GP的机会。对于现在的多数应用而言Q运行时的多态能力显得尤为重要。而现有的GP无法在这个层面发挥作用,以至于我q个“GPq?#8221;也不得不灰溜溜地声称“用OOP构徏pȝQ用GP优化代码”?br>

    然而,不久前,在TopLanguage group上的一ơ讨论,促我们注意到runtime GPq个概念。从中,我们看到了希望——GP runtime化的希望——得GP有望在运行时发挥其巨大的威力Q进一步ؓ软g的设计与开发带来更高的效率和更灉|的结构?br>    在这个新的系列文章中Q我试图q用runtime GP实现一些简单,但典型的案例Q来runtime GP的能力和限制Q同时也可以q一步探讨和展示q种技术的Ҏ?

q行时多?/h3>     现在的应用侧重于交互式的q作形式Q要求Y件在用户输入下作出响应。ؓ了在q种情况下,软g的整体结构的优化Q大量用组件技术,使得软g成ؓ“可组?#8221; 的系l。而接?实现分离的结构Ş式很好地实现了这个目标。多态在此中起到了关键性的作用。其中,以OOPZ表的“动多?#8221;Q也UCؓ “subtyping多?#8221;Q,构徏起在q行时可调控的可l装pȝ。GP作ؓ“静多?#8221;Q运用泛化的cd体系Q大大简化这U系l的构徏Q消除重复劳动。另外还有一U鲜Zh知的多态Ş式,被《C++ Template》的作者David Vandevoorde和Nicolai M. JosuttisUCؓruntime unbound多态。而原来的“动多?#8221;Q即OOP多态,被细化ؓruntime bound多态;“静多?#8221;Q也是模板Q则被称为static unbound多态?br>    不过q种U谓Ҏ引v误解Q主要就是unboundq个词上。在q里unbound和bound是指在编写代码时Q一个symbol是否同一个具体的cd bound。从q点来看Q由于GP代码在编写之Ӟ面向的是泛型Q不是具体的cdQ那么GP是unbound的。因为现有的GP是编译期的技术,所以是 static的。OOP的动多态则必须针对一个具体的cd~写代码Q所以是bound的。但因ؓ动多态可以在q行时确定真正的cdQ所以是runtime 的。至于runtime unboundQ以往只出现在动态语a中,比如SmallTalk、Python、RubyQ一UŞ象地U谓?#8220;duck-typing”多态。关于多态的更完整的分类和介l可以看q里?br>    通过动态语a机制实现的runtime unboundQ存在的性能和类型安全问题。但当我们将GP中的concept技术推q到runtime时会发现Qrungime unbound可以拥有同OOP动多态相当的效率和类型安全性,但却h更大的灵zL和更丰富的Ҏ。关于这斚wQ我已经写过一?a >文章 Q大致描qC一U实现runtime concept的途径Q本文的附录里,我也l出了这Uruntime concept实现的改q)?

Runtime Concept

    Runtime concept的实现ƈ不会很复杂,基本上可以沿用OOP中的“虚表”技术,q且可以更加单。真正复杂的部分是如何在语言层面表达Uruntime GPQ而不对已有的static GP和其他语aҎ造成q扰。在q里Q我首先建立一个基本的ҎQ然后通过一些案例对其进行检验,在发现问题后再做调整?br>    考虑到runtime concept本n也是conceptQ那么沿用static concept的定义Ş式是不会有问题的Q?br>      concept myconcept<T> {
          T& copy(T& lhs, T const& rhs);
          void T::fun();
          ...
      }
    具体的concept定义和用规则,可以参考C++0x?a >concept提案?a >q篇文章 Q以?a >q篇文章 ?br>    另一斚wQ我们可以通过concept_map符合一个concept的类型绑定到该concept之上Q?br>      concept_map myconcept<MyType> {}
    相关内容也可参考上q文件?br>    有了concept之后Q我们便可以用它们约束一个模板:
      template<myconcept T>void myfun(T const& val); //函数模板
      template<myconcept T>class X  //cL?br>      {
           ...
      };
    到此为止Qruntime concept同static conceptq是同一个事物。它们真正的分离在于使用。对于static concept应用Q我们用一个具体的cd在实例化Q特化)一个模板:
      X<MyType> x1;  //实例化一个类模板
      MyType obj1;
      myfun(obj1);  //~译器推导obj1对象的类型实例化函数模板
      myfun<MyType>(obj1);  //函数模板的显式实例化
    现在Q我们将允许一U非常规的做法,以runtime concept成ؓ可能Q?strong>允许使用concept实例化一个模板,或定义一个对?/strong>?br>      X<myconcept> x2;
      myconcept* obj2=new myconcept<MyType>;
      myfun(obj2);  //此处Q编译器会生成runtime版本的myfun
    q里的含义非常明:对于x2Q接受Q何符合myconcept的类型的对象。obj2是一?#8220;动态对?/strong>”Q这里将runtime concept引入的U不知道真实cdQ但W合某个concept的对象称?#8220;动态对?#8221;。而类型明已知的对象成ؓ“静态对?#8221;Q,W合myconcept要求。至于实际的cdQ随便,只要W合myconceptp?br>    q种情Ş非常cM于传l动多态的interface。但是,它们有着Ҏ的差异。interface是一个具体的cdQƈ且要求类型通过l承q种形式实现q个接口。而concept则不是一U类型,而是一U?#8220;泛型”——具备某U特征的cd的抽象(或集合)Q不需要在cd创徏时立M接口l定。与 concept的绑定(concept_mapQ可以发生在M时候。于是,runtime concept实际上成Z一U?strong>非R入的接口。相比interfaceq种侵入型的接口Q更加灵zM捗?br>    通过q样一U做法,我们便可以获得一U能够在q行时工作的GPpȝ?br>    在此基础上,Z便于后箋案例展开Q进一步引入一些有用的Ҏ:
  1. 一个concept的assosiate type被视Z个concept。一个concept的指?引用Q?em>concept_id*/concept_id&Q含义是指向一个符?em>concept_id的动态对象,其实际类型未知)Q都被视作concept。一个类模板用concept实例化后Q逻辑上也是一个concept?
  2. 动态对象的创徏。如果需要在栈上创徏动态对象,那么可以使用语法Q?em>concept_id<type_id> obj_id; q里concept_id是concept名,type_id是具体的cd名,obj_id是对象名U。这P便在栈上创徏了一个符?em>concept_id的动态对象,其实际类型是type_id?br>如果需要在堆上创徏动态对象,那么可以用语法:concept_id* obj_id=new concept_id<type_id>; q实际上可以看作“concept指针/引用”?
  3. concept推导Q编译期Q。对于表辑ּconcept_id obj_id=ExpQ其?em>Exp是一个表辑ּQ如果表辑ּExp的类型是具体的类型,那么obj_id代表了一个静态对象,其类型ؓExp的类型。如果表辑ּExp的类型是conceptQ那?em>obj_id是一个动态对象,其类型ؓExp所代表的concept?br>那么如何定Exp是具体类型还是conceptQ可以用这么一个规则:如果Exp中涉及的对象Q比如函数的实参、表辑ּ的操作数{等Q只要有一个是动态对象(cd是conceptQ,那么Exp的类型就是conceptQ反之,如果所有涉及的对象都是静态对象(cd为具体的cdQ,那么Exp的类型ؓ相应的具体类型。同L规则适用于concept*或concept&?
  4. concept转换。类似在cȝl承l构上执行{换。refined concept可以隐式地{换成base conceptQ反q来必须昑ּ地进行,q且通过concept_cast操作W执行。兄弟concept之间也必通过concept_cast转换?
  5. Zconcept的重载,也可以在runtime时执行,实现泛化的dynamic-dispatch操作?/li>

    下面Q就开始第一个案例?

案例Q升U的坦克

    假设我们做一个游戏,主题是开坦克打仗。按游戏的惯例,消灭敌h可以得到U分Q积分到一定数量,便可以升U。ؓ了简便v见,我们只考虑对主炮升U。第一U的ȝ?0mm的;W二U的ȝ升?20mm。主炮分两种Q一U只能发穿甲弹Q另一U只能发高爆弹。因此,坦克也分ZU:能打I甲弹的和能打高爆弹的?br>    Z使代码容易开发和l护Q我们考虑采用模块化的方式Q开发一个坦克的框架Q然后通过更换不同的主炮,实现不同U类的坦克和升Q?br>      //一些基本的concept定义
      //炮弹头concept
      concept Warheads<typename T> {
          double explode(TargetType tt); //炮弹爆炸Q返回杀伤率。不同弹_对不同类型目标杀伤率不一栗?br>      }
      //炮弹conceptQ我们关心的当然是弹_所以用Warheads定义一个associate type
      concept Rounds<typename T> {
          Warheads WH;
          ...
      }
      //ȝconcept
      concept Cannons<typename T> {
          Rounds R;
          void T::load(R& r); //装填炮弹Qload之后炮弹会存攑֜炮膛里,不能再loadQ除非把炮弹打出?br>          R::WH T::fire();   //开炮,q回弹头。发后炮膛变空Q可以再load
      }
      //cd和模板定?br>      //坦克cL?br>      template<Cannons C>
      class Tank
      {
          ...
      public:
          void load(typenam C::R& r) {
              m_cannon.load(r);
          }
          typename C::R::WH fire() {
              return m_cannon.fire();
          }
      private:
          C m_cannon;
      };
      //ȝcL?br>      template<Rounds Rd>
      class Cannon
      {
      public:
          typedef Rd R;
          void load(R& r) {...}
          typename R::WH fire() {...}
      }
      template<Rounds Rd> concept_map Cannons<Cannon<Rd>>{}
      //炮弹cL?br>      template<Warheads W>
      class Round
      {
      public:
          typedef W WH;
          static const int caliber=W::caliber;
          W shoot() {...}
          ...
      };
      template<Warhead W> concept_map<Round<W>>{}
      //弹头cL板,通过traits把各cd头的不同行ؓ弹头的代码框架分,使类?#8220;可组?#8221;
      concept WH_Traits<T> {
          return T::exploed(int, TargetType, double, Material);
      }
      template<WH_Traits wht, int c>
      class Warhead
      {
      public:
          const static int caliber=c;
          double explode(TargetType tt) {
              return wht::exploed(c, tt, ...);
          }
          ...
      };
      template<WH_Traits WHT, int c> concept_map<Warhead<WHT, c>>{}
      //弹头traits
      struct KE_WHTraits
      {
          static double exploed(int caliber, TargetType tt, double weight, Material m) {...}
      };
      concept_map<KE_WHTraits>{}
      struct HE_WHTraits
      {
          static double exploed(int caliber, TargetType tt, double weight, Material m) {...}
      };
      concept_map<HE_WHTraits>{}
      //定义各类弹头
      typedef Warhead<KE_WHTraits, 90> WH_KE_90;
      typedef Warhead<KE_WHTraits, 120> WH_KE_120;
      typedef Warhead<HE_WHTraits, 90> WH_HE_90;
      typedef Warhead<HE_WHTraits, 120> WH_HE_120;
      //定义各类炮弹
      typedef Round<WH_KE_90> Round_KE_90;
      typedef Round<WH_KE_120> Round_KE_120;
      typedef Round<WH_HE_90> Round_HE_90;
      typedef Round<WH_HE_120> Round_HE_120;
      //定义各类ȝ
      typedef Cannon<Round_KE_90> Cannon_KE_90;
      typedef Cannon<Round_KE_120> Cannon_KE_120;
      typedef Cannon<Round_HE_90> Cannon_HE_90;
      typedef Cannon<Round_HE_120> Cannon_HE_120;
      //定义各类坦克
      typedef Tank<Cannon_KE_90> Tank_KE_90;
      typedef Tank<Cannon_KE_120> Tank_KE_120;
      typedef Tank<Cannon_HE_90> Tank_HE_90;
      typedef Tank<Cannon_HE_120> Tank_HE_120;
    于是Q当我们开始游戏时Q就可以按照玩家的别创建坦克对象,q且击Q?br>      //W一U玩ӞN发射90mm高爆炮弹的坦?br>      Tank_HE_90 myTank;
      Round_HE_90 r1;
      myTank.load(r1);
      myTank.fire();
      //W二U玩ӞN发射120mmI甲弹的坦克
      Tank_KE_120 yourTank;
      Round_KE_120 r2;
      yourTank.load(r2);
      yourTank.fire();
      //如果q样Q危险,炮弹不匹配,心炸膛
      myTank.load(r2); //error
    到目前ؓ止,q些代码仅仅展示了静态的GP。concept在这里也只是起到了类型参数约束的作用。但是,在这些代码中Q我们可以明昑֜看到Q在q用GP 的参数化cdҎ之后,可以很容易地q行lg化。对于一l具备类D为和l构特征的类型,我们可以通过模板的类型参敎ͼ差异部分抽取出来,独立成所谓的 “traits”或?#8220;policy”。ƈ且通过traits或policy的组合构成不同的产品。在某些复杂的情况下Qtraits和policyq可以进一步通过traits或policy实现lg化?br>    接下来,很自然地应当展开runtime GP的运用了?br>    一个游戏者是可以升的,Z使得q种升变得更加灉|Q我们会很自然地使用Composite模式。现在,我们可以在Runtime concept的支援下实现GP版的Composite模式Q?br>      //坦克的concept
      concept tanks<T> {
          typename Round;
          void T::load(Round&);
          Round::WH T::fire();
      }
      concept_map tanks<Tank_KE_90>{}
      concept_map tanks<Tank_HE_90>{}
      concept_map tanks<Tank_KE_120>{}
      concept_map tanks<Tank_HE_120>{}
      //坦克构造函数模?br>      template<tanks T>
      T* CreateTank(WHType type, int level) { //WHType是一个枚举表明炮弹种c?br>          switch(level)
          {
          case 1:
              if(type==wht_KE)
                  return new tanks<Tank_KE_90>;
              else
                  return new tanks<Tank_HE_90>;
          case 2:
              if(type==wht_KE)
                  return new tanks<Tank_KE_120>;
              else
                  return new tanks<Tank_HE_120>;
          default:
              throw error("no such tank.");
          }
      }
      //玩家c?br>      class player
      {
      public:
          void update() {
m_pTank=CreateTank(m_tankType, ++m_level);
          }
          ...
      private:
          int m_level;
          WHType m_tankType;
tanks* m_pTank;
      };
    在类player中,使用了一个conceptQ而不是一个类型,来定义一个对象。根据前面提到的concept推导规则Qm_pTank指向一个动态对象,q是静态对象,取决于ؓ它赋值的表达式类型是conceptq是具体cd。在update()函数中,可以看到Qm_pTank通过表达?strong>CreateTank(m_tankType, ++m_level)
赋倹{那么这个函数的q回cdQ将军_m_pTank的类型。CreateTank()是一个模板,q回cd是模板参敎ͼq且是符合concept tanks的类型。关键在于代码中?strong>return new tanks<...>语句。前文已l说q,q种形式是?lt;...>中的cd创徏一个符合tanks的动态对象。所以,CreateTank()q回的是动态对象。那么,m_pTank也将指向一个动态对象。在q行Ӟ当玩家达C定条Ӟ便可以升U。update()成员函数根据玩家的U别重新创徏相应的坦克对象,赋值到m_pTank中?br>    q里Q实际上是利用tanksq个concept描述Q充当类型的公有接口。它所h的特性同动多态的抽象基类是非常相似的。但是所不同的是Q如同我在代码中展现的那Pconcept作ؓ接口Q可以在M时候定义,同类型绑定。而无需象抽象基c那P必须在类型定义之前定义。于是,q种非R入式的接口相比抽象基cL有更加灵z自qҎ?br>    然而,事情q没有完。在q一步深化坦克案例后Q我们还发现runtime GP拥有更加有趣和重要的Ҏ?br>    坦克开炮ؓ的是d目标。对目标的毁伤情늛接关pd玩家的生存和得分。所以,我们必须对射dQ目标的损毁情况q行计算。于是编写了q样一l函敎ͼ
      double LethalityEvaluate(Target& t, double hitRate, WH_KE_90& wh) {...}
      double LethalityEvaluate(Target& t, double hitRate, WH_HE_90& wh) {...}
      double LethalityEvaluate(Target& t, double hitRate, WH_KE_120& wh) {...}
      double LethalityEvaluate(Target& t, double hitRate, WH_HE_120& wh) {...}
    Target是目标;hitRate是命中率Q根据坦克和目标的位|、射d数综合计获得(如果惌更真实,可以加上风向、风力、气温、湿度、v拔等{因素)Qwh是发射出来的炮弹了。函数返回杀伤率。如此,我们便可以在击之后q行评估了:
      double l=LethalityEvaluate(house, hr, myTank.fire());
     现在Q游戏需要进行一些扩展,增加一个升U,允许坦克升到第三。到了第三极Q主炮的口径升到头了,但可以升U功能,可以发射I甲弹和高爆式V这P我们需要一?#8220;两用”的主炮类。但是,实际上ƈ不需要直接做q么一个类Q只需要用“两用”的弹药来实例化Cannon模板Q?br>      concept Warheads120<T> : Warheads<T> { //120mm炮弹头concept
          double LethalityEvaluate(Target& t, double hitRate, T& wh);
      }
      concept Rounds120<T> : Rounds<T> {}
      concept_map Warheads120<WH_KE_120> {}  //120mm的穿甲弹属于Warheads120
      concept_map Warheads120<WH_HE_120> {}  //120mm的高爆弹属于Warheads120
      template<WH120 WH> concept_map Rounds120<Round<WH>> {} //所有弹头是Warheads120的炮弚w是属于Rounds120
      typedef Canon<Rounds120> Cannon120; //用Rounds120实例化Cannon模板Q得?#8220;两用”ȝ
    一堆炫目的concept和concept_map之后Q得到Rounds120Q就是所谓的“两用”弹药。作Z个conceptQ它同两U类型map 在一P实际上就成了q两个类型的接口。当我们使用Rounds120实例化Cannon<>模板Ӟ也就创徏了一?#8220;两用的主?#8221;Q?Rounds120弹药的主炮)。如果用q个Cannon120实例化Tank模板Q那么就可以得到W三U坦克(装上Cannon120ȝ的坦克就是第三Q:
      typedef Tank<Cannon120> TankL3;
    于是Q我们可以用不同的120mm弹药装填ȝQƈ且发相应的炮弹Q?br>      TankL3 tank_l3;
      Round_KE_120 ke_round;  //创徏一枚穿甲弹
      Round_HE_120 he_round;  //创徏一枚高爆弹
      tank_l3.load(ke_round);  //装填I甲?br>      tank_l3.fire();               //发射I甲?br>      tank_l3.load(he_round);  //装填高爆?br>      tank_l3.fire();               //发射高爆?br>    现在Q我们把注意力从消灭敌hQ{UdTankL3::load()的参数类型和TankL3::fire()的返回类型上。在一U和二坦克Q类?Tank_KE_90{)上,load()成员的参数类型是Round_KE_90{具体的cdQ而fire()的返回类型亦是如此。但TankL3是用 Cannon120实例化的Q而Cannon120是用Rounds120q个concept实例化的。根据Tank<>模板的定义, load()成员的参数类型实际上是模板参C的一个associate type。而这个associate type实际上就是Rounds120。这意味着load()实例化后的签名是Qvoid load(Rounds120& r)Q这里暂且允许concept作ؓcd参数使用Q。只要符合Rounds120的类型都可以作ؓ实参传递给load()成员。同Pfire()成员的返回类型来自于Round120上的associate typeQ也是个concept。因此,fire()实例化后的签名是QWarheads120 fire()?br>    接下来值得注意的是fire()成员。它q回cd是一个conceptQ那么返回的是一个动态对象。在q行Ӟ它可能返回WH_KE_120的实例,也可能返回WH_HE_120的实例,取决于运行时load()函数所装填的炮弹类型。当我们采用LethalityEvaluate()函数对该炮弹的杀伤情况进行评估将会出现比较微妙的情况Q?br>      double x=LethalityEvaluate(hisTank, hr, tank_l3.fire());
    q时候,~译器应当选择哪个LethalityEvaluate()Q由于tank_l3.fire()q回的是一个动态对象,具体的类型编译时不知道。实际上Q在正宗的静态语a中,q样的调用根本无法通过~译。当Ӟ~译器可以通过runtime reflect获得cd信息Q然后在LethalityEvaluate()的重载中匚w正确的函数。然而,q种动态语a做法会造成性能上的问题Qؓ静态语a所不屑?br>    但是Q在q里Q在runtime concept的作用下Q我们可以ɘq种调用成ؓ静态的、合法的Qƈ且是高效的。请注意我在concept Warhead120的定义中加入了一个associate functionQdouble LethalityEvaluate(Target& t, double hitRate, T& wh);。runtime concept会很忠实地将concept定义中的associate function构造到一个函数指针表Q我UC为ctableQ中。(详细情况L本文附录?a >q篇文章的附录)。因此,与tank_l3.fire()q回的动态对象实际类型对应的LethalityEvaluate()函数版本的指针正老老实实地w在相应的ctable里。所以,我们可以直接从动态对象上获得指向ctable的指针,q且扑և相应的LethalityEvaluate()函数指针Q然后直接调用即可。比如:
      tank_l3.load(ke_round);
      double x=LethalityEvaluate(hisTank, hr, tank_l3.fire());
    在这些代码的背后Qke_round通过load()装填入主炮后Q便摇n变成了一个动态对象。编译器会ؓ它附加上指向ctable的指针,然后在调?fire()的时候返回指向这个动态对象的引用。此Ӟ~译器发现这个动态对象所对应的Warhead120 concept上已l定义了一个名为LethalityEvaluate()的associate functionQƈ且签名与当前调用相符。于是,便可以直接找到ctable中LethalityEvaluate()对应的那个函数指针,无所ֿ的调用。由于一个concept的associate function肯定是同实际cd匚w的函数版本。比如,对于WH_HE_120而言Q它的associate function LethalityEvaluate()是版本:double LethalityEvaluate(Target& t, double hitRate, WH_HE_120& wh) {...}。其他版本的LethalityEvaluate()都无法满concept Warhead120施加在类型WH_HE_120上的U束?br>    q个Ҏ就使得runtime concept作ؓ接口Q相比动多态的抽象接口Q具有更大的灉|性。抽象接口只表达了类的成员,以及cLw的行ؓQ无法表辄型同其他cd的互动关p,或者说cd间的交互。而concept同时描述了成员函数和相关的自由函敎ͼ包括操作W)Q得类型间的关pM可以通过接口直接获得Q无需再通过 reflect{间接的动态手Dc?br>    q一点在处理内置cdQ如int、floatQ、预|类型(某些库中的类型)、第三方cd{不易或无法修改的类型有臛_重要的作用。在OOP下,我们无法输出一?#8220;整数”Q或许是short、或许是longQ甚xunsinged longlong。ؓ此,我们要么把它们{换成一?#8220;最基础cd”QC/C++的void*Q或C#的Object*Q,然后q用rtti信息q行cd转换Q再做处理;要么使用variantq种cd包装Q就像COM中的那样Q,然后Z们全面定义一套计库。但runtime concept不仅仅允许输?#8220;整数”q样一个动态对象,而且q将相关的各U操作附在动态对象之上,使之无需借助rtti或者辅助类型也可进行各cd理,如同处理具体类型的对象那样?br>    但是Q在q里我仅仅考察了针对一个类型的conceptQ暂且称之ؓ一元conceptQ,q未涉及两个和两个以上类型的conceptQ暂且称为多?conceptQ或n-元conceptQ。在实际开发中Q多数操作都会涉及多个对象,比如两个数相加、一U类型{换成另一U。此Ӟ我们会面对多元?concept。但是多元的runtime concept的特性还不清楚,q需要进一步研I分析?

ȝ

    本文初步展示了在引入runtime concept之后QGP的动态化Ҏ。归Uv来有以下几点Q?
  1. static GP和runtime GP之间在Ş式上完全l一Q两者可以看作同一U抽象机制的不同表现。因此,我们在构造类型、函数等代码实体的时候,q不需要考虑它们来需要作为static使用Q还是runtime使用。static和runtime的控制完全取决于q些代码实体的用方式。这很好地减少了Y仉目早期设计,以及库设计的前瞻性方面压力?
  2. runtime concept作ؓ非R入式的接口,可以非常灉|C用。我们无需在代码编写的一开始就_地定义好接口Q可以先直接~写功能cdQ逐步构徏软gl构。需要时再定义接口(conceptQ,q可以在M时候与cdl定。接口的定制可以成ؓ一个逐步推进的过E,早期的接口设计不生的不良影响相应地弱化了?
  3. runtime concept相比动多态的抽象接口更加自由。concept可以对类型的成员函数、自由函数、类型特征等{方面的Ҏ作出描q。在runtime化之后,相关自由函数成ؓ了接口的一部分。更q一步规U了cd在整体Y件的代码环境中的行ؓ特征。同Ӟ也ؓ动态对象的讉K提供更多的信息和手段?
  4. concept不仅实现cd描述Q还可以q一步描q类型之间的关系。这大大完善了抽象体pR特别在runtime情况下,q种更宽泛的cd描述能力可以起到两个作用Q其一Q进一步约束了动态对象的行ؓQ其二,为外界操作和使用cd提供更多的信息,消除或减了cd匚w斚w的抽象惩|。这个方面的更多Ҏ尚不清楚,q需要更q一步地深入研究?
    lg所qͼ我们可以看到runtime GP在不损失性能的情况下Q具备相比动多态更灉|、更丰富的手Dc从Ҏ上而言Q以concept为核心的GP提供了更基础的抽象体p(关于q方面探讨,L我的q篇文章中关于concept对类型划分的作用部分Q。或者说Qconcept的类型描q和U束作用体现了类型抽象的本质Q而在此基上进一步衍生出static和runtime两种具体的用方式。这也就是所谓:道生一Q一生二?)

附录

Runtime Concept实现Ҏ?/h4>     我在q篇文章附录里,l出了一U实现runtime concept的可能方案。这里,我进一步对q个Ҏ做了一些改q,使其更加_、高效?br>    假设我们有一个conceptQ?br>    concept Shape<T>
    {
        void T::load(xml);
        void T::draw(device);
        void move(T&);
    }
    另外Q还有一个代表圆的conceptQ?br>    concept Cycles<T> :
        CopyConstructable<T>,
        Assignable<T>,
        Swappable<T>,
        Shape<T>
    {
        T::T(double, double, double);
        double T::getX();
        double T::getY();
        double T::getR();
        void T::setX(double);
        void T::setY(double);
        void T::setR(double);
    }
    现在有类型CycleQ?br>    class Cycle
    {
    public:
        Cycle(double x, double y, double r);
        Cycle(Cycle const& c);
        Cycle& operator=(Cycle const& c);
        void swap(Cycle const& c);
        void load(xml init);
        void draw(device dev);
        double getX();
        double getY();
        double getR();
        void setX(double x);
        void setY(double y);
        void setR(double r);
    private:
        ...
    };
    我们类型Cycle map到concept Cycles上:
      concept_map Cycles<Cycle>{}
    当我们创建对象时Q将会得到如下图的结构:

runtime concept-2

    concept表(concept listQ不再同对象攑֜一P而是同一个类型的cd信息攑֜一赗一同放|的q有ctable。ctable中每个对应的conceptw有一个指?concept表的指针Q也可以指向cd信息_Q用以找到concept listQ执行concept cast。动态对象,或动态对象的引用/指针上只需附加一个指向相应的concept的指针即可。相比前一个方案,内存利用率更高?/p>

longshanks 2008-01-06 17:17 发表评论
]]>被误解的C++&mdash;&mdash;汉尼?/title><link>http://www.shnenglu.com/longshanks/archive/2007/12/17/38708.html</link><dc:creator>longshanks</dc:creator><author>longshanks</author><pubDate>Mon, 17 Dec 2007 03:28:00 GMT</pubDate><guid>http://www.shnenglu.com/longshanks/archive/2007/12/17/38708.html</guid><wfw:comment>http://www.shnenglu.com/longshanks/comments/38708.html</wfw:comment><comments>http://www.shnenglu.com/longshanks/archive/2007/12/17/38708.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.shnenglu.com/longshanks/comments/commentRss/38708.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/longshanks/services/trackbacks/38708.html</trackback:ping><description><![CDATA[<h1 align="center">被误解的C++——汉拔</h1> <p align="center">by 莫华? </p> <p><em>    公元?16q??日,意大利东部^原,一个叫做坎的地方Q两支大军摆开阵势Q准备决一L。一Ҏ׃|斯和瓦|两位执政官率领的罗马hQ另一方则是伟大的军事天才汉尼?巴卡率领的迦太基军队及其同盟。罗马h过8万,而迦太基仅有4万余人。然而到了傍晚,|马d击|Q?万h被杀Q仅有少数得以逃脱。这是著名的坎g战。经此一役,Q外加先前进行的特利比亚和特拉西梅诺湖会战)Q罗马h元气大伤Q成q公民损p五分之一。部分城邦背叛罗马,西西里也发生起义。罗马已l到了摇摇欲坠的地步?/em> </p> <p><em>    汉尼拔的q些胜利Q完全得益于先前的一ơ异乎寻常的q征。公元前218q_汉尼拔率领军队,从新q太基城Q西班牙Q出发,越比利牛斯山,q入南高卢地域。在他面前有两条路可赎ͼ越阿尔俾斯山,或者沿岸q入意大利。但是,当时|马人已在沿地区部|了两支部队Q准备拦截汉拔。而且Q罗马h的v军优势,使得他们可以在Q何时候将一支部队登陆在他的背后。而翻阿俾斯山Q则是一条及其艰险的道\Q更何况是在冬天?/em> </p> <p><em>    汉尼拔选择了阿俾斯山。他甩开了罗马hQ从圣贝纳德和日内瓦山之间过阿尔俾斯山,q入意大利境内。此Ӟ|马Z失去了战略纵深,一把尖刀已经深深地插入他们的腹内...</em> </p> <p>  </p> <p>    C++的发展史上,也有着如同汉尼拔翻阿俾斯山q征。一切还得从C with Class时代说v? </p> <p>    Bjarne曄反复Q他创徏C++为的是将Simular的抽象能力同C的性能l合h。于是,在C语言的基上,诞生了一U拥有类、ѝ重载等{面向对象机制的语言。在q个阶段QC++提供了两个方面的抽象能力。一U是数据抽象Q也是数据所要表辄含义通过cd以及依附于类型上的成员表q。另一U则是多态,一U最原始的多态(重蝲Q? </p> <p>    数据抽象Q通过被称?#8220;抽象数据cdQADTQ?#8221;的技术实现。ADT的一U方案,是cR类所提供的封装性将一个数据实体的外在特征Q或者说语义的表qŞ式,同具体的实现Q比如数据存储Ş式,分离。这h增加的中间层数据的使用者同数据的实现者隔,使得他们使用共同的约定语义工作,不再怺了解彼此的细节,从而得两者得以解耦? </p> <p>    多态则是更加基更加重要的一U特性。多态得我们得以用同一U符号实现某U确定的语义。多态的_N在于Q以一UŞ式表达一U语义。在此之前,我们往往被迫使用不同的符h代表同一U抽象语义,为的是适应强类型系l所施加的约束。比如: </p> <blockquote> <p>//代码#1<br>int add_int(int lhs, int rhs);<br>float add_float(float lhs, float rhs);</p> </blockquote> <p>    很显Ӟq两个函数表辄语义分别?#8220;把两个intcd值加在一?#8221;?#8220;把两个floatcd值加在一?#8221;。这两个语义抽象h都表达了一个意思:加? </p> <p>    我们在做术题的时候是不会被计算的数字是整数q是实数。同P如果能够在编E的时候,不考虑术操作对象的类型,只需兛_谁和谁进行什么操作,那么会方便得多。当C++引入重蝲后,q种愿望便得以实玎ͼ </p> <blockquote> <p>//代码#2<br>int add(int lhs, int rhs);<br>float add(float lhs, float rhs);</p> </blockquote> <p>    重蝲使得我们只需兛_“?#8221;q个语义Q至于什么类型和什么类型相加,则由~译器根据操作数的类型自动解析? </p> <p>    从某U意义上_重蝲是被长期忽视Q但却极为重要的一个语aҎ。在多数介绍OOP的书c中Q重载往往被作为OOP的附属品Q放在一些不L的地斏V它的多态本质也被动多态的人造光环所N蔽。然而,重蝲的重要作用却在实践中潜移默化C现出来。重载差不多可以看作语言q入C抽象体系的第一步。它的实际效用甚臌过被广为关注的OOPQ而不会像OOP那样在获得抽象的同时Q伴随着不小的副作用? </p> <p>     随着虚函数的引入QC++开始具备了颇具争议的动多态技术。虚函数是一U依附于c(OOP的类型基Q的多态技术。其技术基是后期绑定(late-bindingQ。当一个类Dl承自类BӞ它有两种Ҏ覆盖QoverrideQB上的某个函数Q? </p> <blockquote> <p>//代码#3<br>class B<br>{<br>public:<br>    void fun1();<br>    virtual void fun2();<br>}; </p> <p>class D:public B<br>{<br>public:<br>    void fun1();<br>    void fun2();<br>};</p> </blockquote> <p>    当承类D中声明了同基cB中成员函数相同函数名、相同签名的成员函数Q那么基cȝ成员函数被覆盖。对于基cȝ非虚成员函数Q承类会直接将光蔽。对于类型D的用者,fun1代表了D中所赋予的语义。而类型D的实例,可以隐式地{换成cdB的引用bQ此时调用b的fun1Q则执行的是cB的fun1 定义Q而非cD的fun1Q尽此时b实际指向一个D的实例? </p> <p>    但是Q如果承类覆盖了基cȝ虚函敎ͼ那么得到相反的l果Q当调用引用b的fun2Q实际上却是调用了D的fun2定义。这表明Q覆盖一个虚函数Q将会在l承cd基类之间的所有层ơ上执行覆盖。这U彻底的、全方位的覆盖行为,使得我们可以在承类上修饰或扩展基类的功能或行ؓ。这便是OOP扩展机制的基。而这U技术被UCؓ动多态,意思是基类引用所表达的语义ƈ非取决于基类本nQ而是来源于它所指向的实际对象,因此它是“多?#8221;的。因Z个引用所指向的对象可以在q行时变换,所以它?#8220;?#8221;的? </p> <p>    随着动多态而来的一?#8220;副?#8221;Q却事实上成ZOOP的核心和支柱。虚函数?#8220;动多?#8221;Ҏ将我们引向一个极端的情况Q一个都是虚函数的类。更重要的,q个cM的虚函数都没有实玎ͼ每个虚函数都未曾指向一个实实在在的函数体。当Ӟq样的类是无法直接用的。有的是,q种被称?#8220;抽象基类”的类Q?strong>q</strong>我们l承它,q?#8220;替它”实现那些没有实现的虚函数。这P对于一个抽象基cȝ引用Q多态地拥有了承类的行为。而反q来Q抽象基cd际上起到?strong></strong>l承cd现某些特定功能的作用。因此,抽象基类扮演?strong>接口</strong>的角艌Ӏ接口具有两重作用:一、约束承类Q实现者)q其实现预定的成员函数Q功能和行ؓQ;二、描qCl承cd定拥有的成员函数Q功能和行ؓQ。这两种作用促接口成ؓ了OOP设计体系的支柱? </p> <p>    C++在这斚w的进步,使其成ؓ一个真正意义上具备C抽象能力的语a。然而,q种q步q“越阿尔俾斯?#8221;。充光也只能算?#8220;越比利牛斯?#8221;。对于C++而言Q真正艰苦的q征才刚开始,那o人生畏的“阿尔俾斯?#8221;仍在遥远的前斏V? </p> <p>    同汉拔一P当C++一脚迈?#8220;C抽象语言׃?#8221;后,侉K临两U选择。或者在原有基础上修修补补,成ؓ一UOOP语言Q或者l前q,越那险峻的山峰。C++的汉拔——Bjarne Stroustrup——选择了后者? </p> <p>    从D&E的描qC我们可以看到Q在C++的原始设计中已l考虑“cd参数”的问题。但直到90q代初,才真正意义上地实C模板。然而,模板只是W一步。诸如Ada{语a中都有类似的机制Q泛型,genericQ,但ƈ未对当时的编E技术生根本性的影响? </p> <p>    关键性的成果来源于Alex Stepanov的A献。Stepanov在后来被UCؓstl的算?容器库上所做的努力Q得一U新兴的~程技术——泛型编E(Generic ProgrammingQGPQ——进入了Z的视野。stl的生对C++的模板机制生了极其重要的媄响,促了模板特化的诞生。模板特化表面上是模板的辅助Ҏ,但是实际上它却是?#8220;cd参数”更加本质的机能? </p> <p>    假设我们有一l函数执行比较两个对象大的操作Q? </p> <blockquote> <p>//代码#4<br>int compare(int lhs, int rhs);<br>int compare(float lhs, float rhs);<br>int compare(string lhs, string rhs);</p> </blockquote> <p>    重蝲使得我们可以仅用compare一个函数名执行不同cd的比较操作。但是这些函数具有一L实现代码。模板的引入Q得我们可以消除这U重复代码: </p> <blockquote> <p>//代码#5<br>template<typename T> int compare(T lhs, T rhs) {<br>    if(lhs==rhs)<br>        return 0;<br>    if(lhs>rhs)<br>        return 1;<br>    if(lhs<rhs)<br>        return -1;<br>}</p> </blockquote> <p>    q样一个模板可以应用于McdQ不但用一个符可达了一个语义,而且用一个实C替了诸多重复代码。这便是GP的基本作用? </p> <p>    接下来的变化Q可以算作真正意义上?#8220;d”了? </p> <p>    如果有两个指针,分别指向两个相同cd的对象。此时如果我们采用上qcompare函数模板Q那么将无法得到所需的结果。因为此时比较的是两个指针的|而不是所指向的对象本w。ؓ了应付这U特D情况,我们需要对compare?#8220;特别处理”Q? </p> <blockquote> <p>//代码#6<br>template<typename T> int compare(T* lhs, T* rhs) {<br>    if(*lhs==*rhs)<br>        return 0;<br>    if(*lhs>*rhs)<br>        return 1;<br>    if(*lhs<*rhs)<br>        return -1;<br>}</p> </blockquote> <p>    q个“Ҏ版本”的compareQ对于Q何类型的指针作出响应。如果调用时的实参是一个指针,那么q个“指针?#8221;的compare会得到优先匚w。如果我们将compareҎ下面的实玎ͼ那么׃出现非常有趣的行为: </p> <blockquote> <p>//代码#7<br>template<typename T><br>struct comp_impl<br>{<br>    int operator()(T lhs, T rhs) {<br>        if(lhs==rhs)<br>            return 0;<br>        if(lhs>rhs)<br>            return 1;<br>        if(lhs<rhs)<br>            return -1;<br>    }<br>};<br>template<typename T><br>struct comp_impl<T*><br>{<br>    int operator()(T* lhs, T* rhs) {<br>        comp_impl<T>()(*lhs, *rhs);<br>    }<br>};<br>template<typename T> int compare(T* lhs, T* rhs) {<br>    comp_impl<T>()(*lhs, *rhs);<br>}</p> </blockquote> <p>    当我们将指针的指针作为实参,调用compareӞ奇的事情发生了Q? </p> <blockquote> <p>//代码#8<br>double **x, **y;<br>compare(x, y);</p> </blockquote> <p>    compare居然成功地剥M两个指针Qƈ且正地比较了两个对象的倹{这个戏法充分利用了cL板的局部特化和特化解析规则。根据规则,是特化的模板,是优先匚w。T*版的comp_impl比T版的更加“特化”Q会得到优先匚w。那么当一个指针的指针实例化comp_implQ则会匹配T*版的 comp_implQ因为指针的指针Q也是指针。T*版通过局部特化机Ӟ剥离掉一U指针,然后用所得的cd实例化comp_impl。指针的指针剥离掉一U指针,那么q是一个指针,又会匚wT*版。T*版又会剥L一U指针,剩下的就是真正可以比较的cd——double。此Ӟdouble已无法与 T*版本匚wQ只能匹配基模板Q执行真正的比较操作? </p> <p>    q种奇妙的手法是蕴含在模板特化中一些更加本质的机制的结果。这U意外获得的“模板衍生产品”可以作一U编译时计算的能力,后来被一?#8220;好事?#8221;发展成独立的“模板元编E?#8221;QTemplate Meta ProgrammingQTMPQ? </p> <p>    管TMP新奇而又奥妙Q但l究只是一U辅助技术,用来弥补C++的一些缺陗做一些扩展,“捡个?#8221;什么的。不q它为我们带来了两点重要的启C:一、我们有可能通过语言本n的一些机Ӟq行元编E;二、元~程在一定程度上可以同通用语言一起用。这些启C对~程语言的发展有很好的指导意义? </p> <p>    模板及特化规则是C++ GP的核心所在。这些语aҎ的强大能力q凭空而来。实际上有一?#8220;q后大手”在冥冥之中操U늝一切? </p> <p>    假设有一个类型系l,包含n个类型:t1,...,tnQ那么这些类型构成了一个集合T={t1,...,tn}。在当我们运用重载技术时Q实际上构造了一l类型的tuple到函数实现的映射Q?lt;ti1,ti2,ti3,...> ->fj()。编译器在重载解析的时候,是按照q组映射L匚w的函数版本。当我们~写了Ş如代?5的模板,那么q当于构徏了映:<T, T,...> ->f0()? </p> <p>    而代?6Q以及代?7中的T*版模板,实际上是构造了一?lt;Tp>->fp()的映。这里Tp是T的一个子集:Tp={t'|t'=ti*, ti∈T}。换句话_特化使泛型体pȝ化了。利用模板特化技术,我们已经能够Q笨拙地Q分辨QҎ、整数、内|类型、内|数l、类、枚丄{类型。具备ؓcd划分的能力,也就是构造不同的cd子集的能力? </p> <p>    现在Q我们便可以构造一?#8220;泛型体系”QG={T} U T U Tp U Ta U Ti U Tf U Tc ...。其中,Tp是所有指针类型,Ta是数l,Ti是整敎ͼTf是QҎQTc是类{等。但是如果我们按照泛化程度,把G中的元素排列开Q{T, Tp, Ta, Ti,...,t1,...,tn}。我们会发现q中间存在一?#8220;断层”。这些断层位于T和Tp{之_以及Tp{与ti{之间等{。这表明在C++98/03中,抽象体系不够完整Q存在缺陗? </p> <p>    所以,到目前ؓ止,C++q没有真正翻阿俾斯山里那座最险峻的山峰。这正是C++0x正在努力做的Q而且胜利在望? </p> <p>    在C++0x中,大牛们引入了first-class的concept支持。concept目前q没有正式的法定描述Q以及合理的中文译Q。通俗地讲Qconcept描述了一个类型的Q接口)特征。说具体的,一个concept描述了类型必d备的公共成员函数Q必d备的施加在该cd上的自由函数Q和操作W)Q以及必d备的其他特征Q通过间接手段Q。下面是一个典型的conceptQ? </p> <blockquote> <p>concept has_equal<T><br>{<br>    bool T::equal(T const& v);<br>};</p> </blockquote> <p>    q个concept告诉我们它所描述的类型必L一个equal成员Q以另一个同cd的对象ؓ参数。当我们这个concept施加在一个函数模板上Qƈ作ؓ对类型参数的U束Q那么就表明了这个模板对cd参数的要求: </p> <blockquote> <p>template<has_equal T>bool is_equal(T& lhs, T const& rhs) {<br>    return lhs.equal(rhs);<br>}</p> </blockquote> <p>    如果实参对象的类型没有equal成员Q那么is_equal会拒绝~译通过Q这不是我要的! </p> <p>    concept是可以组合的Q正式的术语叫做“refine”。我们可以通过refineq一步构造出U束更强的conceptQ? </p> <blockquote> <p>concept my_concept<T> : has_equal<T>, DefaultConstructable<T>, Swappable<T> {}</p> </blockquote> <p>    refine获得的concept会“l承”那些“基concept”的所有约束。作为更l致的组合手D,conceptq可以通过!操作W?#8220;L”某些内涵的conceptU束Q? </p> <blockquote> <p>concept my_concept1<T> : has_equal<T>, !DefaultConstructable<T> {}</p> </blockquote> <p>    q个concept要求cd具备equal成员Q但不能有默认构造函数? </p> <p>    通过q些手段Qconcept可以“无限l分”cd集合T。理ZQ我们可以制造一q串只相差一个函数或者只相差一个参数的concept? </p> <p>    一个concept实际上就是构成类型集合T的划分的U束QTx={ti| Cx(ti)==true, ti∈T}。其中Cx是concept所构造的U束。不同的concept有着不同范围的约束。这P理论上我们可以运用concept枚D出类型集?T的所有子集。而这些子集则正好填补了上qG中的那些断层。换句话_conceptl化了类型划分的_度Q或者说泛型的粒度。?#8220;L”的泛型系l变?#8220;q箋”的? </p> <p>    当我们运用conceptU束一个函数模板的cd参数Ӟ相当于用concept所描述的类型子集构Z个映:<Tx1,Tx2,...>->fx()。凡是符合tuple <Tx1,Tx2,...>的类型组合,对应fx()。所以,从这个角度而言Q函数模板的特化Q包括conceptQ可以看作函数重载的一U扩展。在concept的促q下Q我们便可以把函数模板特化和函数重蝲l一在一个体pM处理Q用共同的规则解析? </p> <p>    在目前阶D,C++差不多已l登上了“抽象阿尔俾斯?#8221;的顶峰。但是就如同汉尼拔进入意大利后,q需要面对强盛的|马共和国,与之作战那样。C++的面前还需要进一步将优势化作胜利。要做的事还很多Q其中最重要的,当属构徏Runtime GP。目前C++的GP是编译时机制。对于运行时x的Q务,q需要求助于OOP的动多态。但是C++领域的大牛们已经着手在Runtime GP和Runtime Concept{方面展开努力。这斚w的最新成果可以看<a >q里</a>?a >q里</a>?a >q里</a>。相信经q若q年的努力后QGP会完全的成熟,成ؓ真正L的编E技术? </p> <p> </p> <p><em>    坎尼会战之后Q汉拔已经拥有了绝对的优势。罗马h已经战|Q他同罗马城之间已经没有M强大的敌对力量,|马Z已经闻风丧胆Q几无斗志。但是,汉尼拔却犯下了或o他一生后悔的错误。他放过了罗马城Q{而攻ȝ马的南部城邦和同盟。他低估了罗马h的意志,以及|马同盟的牢固程度。罗马h很快l束了最初的混ؕQQ命了新的执政官,采用了坚壁清野、以柔克刚的新战略。随着旉的推U,汉尼拔和他的军队限于孤立无援的境圎ͼ被迫Z生存而作战。尽迫降ƈ占领了几个罗马城市,但是l究无法再次获得l予|马命一ȝZ?/em></p> <p><em>    汉尼拔的战略错误实际上在从新q太基城出发之前已经注定。因为汉拔对罗马h的远征的Ҏ目的qLq占领罗马,而是通过打击|马Q削׃们的势力Q瓦解他们的联盟。以辑ֈL{订和^协议的目的。然而这U有限战略却使导致了他的最l失败?/em></p> <p><em></em> </p> <p>    不幸的是QC++或多或少地有着同汉拔一L战略错误。C++最初的目的基本上仅仅局限于“更好的C”。ƈ且全面兼容C。这在当时似乎很合理Q因为C可以作最成功?#8220;底层高语言”Q拥有很高的性能和灵zL。但是,C的设计ƈ未考虑来会有一个叫?#8220;C++”的语a来对其进行扩展。结果很多优点对于C而言是优点,但却成了C++的负担。比如,C大量使用操作W表达语法结构,对于C而言昑־非常z,但对于C++Q则使其被迫大规模复用操作符Qؓ其后出现的很多语法缺陷埋下了伏笔。这一点上QAda做得相对成熟些。它从Pascal那里l承了主要语法,但不考虑兼容。这使得Ada更加完整Q易于发展。新语言是新语aQ过分的兼容是镣铐,不是优势。而且Q合理地l承语法Q同样可以吸引众多开发者。从l验来看Q程序员对于语法变化的承受能力还是很强的。他们更多地兛_语言的功能和易用性?/p> <p>    另一斚wQC++最初ƈ未把目标定在“创徏一U高度抽象,又确保性能的语a”。纵观C++的发展,各种抽象机制q在完整的规划或\U图的指g加入语言。所有高U特性都是以“La战术”零打敲地加入语a。从某种E度上来看,C++更像是一U实验性语aQ而非工业语言。C++的强大功能和优点是长期积累获得的Q而它的诸多缺陷也是长期添加特性的l果?/p> <p>    汉尼拔和C++l予我们一个很好的教训。对于一个试囑֜1?0q后依然健康成长的语aQ那么就必须在最初便拥有明确的目标和技术发展规划。对于以往的语aҎ应当择优而取Q不能照单全收。ƈ且在技术上拥有_的前L。我们知道,技术前L是很难做到的,毕竟技术发展太快。如果做不到Q那得有够的力对过ȝ东西加以取舍。所?#8220;舍小大Q弃子争?#8221;?/p> <p>    M而言QC++在抽象机制的发展斚wQ还是成功的。尽伴随着不少技术缺P但是C++的抽象能力在各种语言中可U得上出cL萃。而且C++q在发展Q它未来会发展成什么Ş态,不得而知。但是,无论C++是l修修补补,q是Ҏ性地变革Q它的抽象能力都会不折不扣地保留Qƈ且不断完善和增强?/p> <p> </p> <p><em>    坎尼会战之后Q汉拔又打q几ơ小规模的胜仗。但l过长期的作战,也得不到q太基的支援Q汉拔的力量越来越弱,只能在意大利半岛上勉强生存。罗马很快恢复了元气Q改革了军事体系和作战方式,重新掌握了战略主动权。更重要的是Q罗马也有了自己?#8220;汉尼?#8221;——(征服非洲的)普布利乌?#183;U尔内利乌斯·西庇阿(大西庇阿Q。西庇阿被派往北非大陆Q直接攻击迦太基人的老l。汉拔被召回,在扎马与西庇阿摆开阵势Q展开一场决战。最l,西庇阿运用从汉尼拔那里学到的战术L了迦太基人,为罗马h赢得了第二次布匿战争的胜利?/em></p> <p><em>    此后Q汉拔在罗马h的通缉之下Q流亡于Ch岸,试图L东山再v的机会。但最l未能如愿,被迫于公元前183q自,享年64岁。有的是,他的老对手,他12岁的西庇阿也于同q去世。一个伟大的传奇此l束?/em></p><img src ="http://www.shnenglu.com/longshanks/aggbug/38708.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/longshanks/" target="_blank">longshanks</a> 2007-12-17 11:28 <a href="http://www.shnenglu.com/longshanks/archive/2007/12/17/38708.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>OOP的黄?/title><link>http://www.shnenglu.com/longshanks/archive/2007/12/06/37915.html</link><dc:creator>longshanks</dc:creator><author>longshanks</author><pubDate>Thu, 06 Dec 2007 09:20:00 GMT</pubDate><guid>http://www.shnenglu.com/longshanks/archive/2007/12/06/37915.html</guid><wfw:comment>http://www.shnenglu.com/longshanks/comments/37915.html</wfw:comment><comments>http://www.shnenglu.com/longshanks/archive/2007/12/06/37915.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.shnenglu.com/longshanks/comments/commentRss/37915.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/longshanks/services/trackbacks/37915.html</trackback:ping><description><![CDATA[    <em>本文来源?a title="TopLanguage group" id="bilj">TopLanguage Group</a> 上的一ơ讨论(<a title="q里" id="g_hv">q里</a> Q?a title="q里" id="nflo">q里</a> ?a title="q里" id="pamp">q里</a> Q。pongba提出QC++的抽象机制ƈ不完善,原因是ؓ了性能而做的折中,未来随着计算能力的提高到一定程度,Zp够忽?/em><em>更好的抽象所带来的负面效?/em><em>。就此诸老大各自提出高见Q受益良多啊。经q讨论,我基本上理解了pongba的想法。但我觉得等待计机的性能提高太消极了。我怿随着~程技术的发展Q这U最优抽象造成的性能损失会来小。这U途径会更快地让Z接受最优抽象Ş式?/em><br> <br>     ?#8220;C++ Template”一书中Q将多态ȝZU主要类型:runtime bound、static unbound和runtime unbound。其中runtime bound是我们通常所说的动多态,OOP的核心支柱(q义上OOPq包括Object BaseQOBQ仅指类型封装等OO的基本特性)Q但有时也会OB和OOP分开QOOP单指以OO为基的动多态。这里用狭义的OOP含义Q; static unbound是静多态,通过模板实现。而runtime unbound则是一U不常见的Ş式。早q的SmallTalkhq种形式Q现在的ruby也引入这U机制?br>     在主的Q静态)语言中,我们会面临两U类型的多态需求:对于~译期可以确定类型的Q用静多态,比如实例化一个容器;对于q行期方能确定类型的Q则使用 动多态。而runtime unbound也可以用于运行期cdx。于是,便有了两U运行期多态。这两种多态的Ҏ和他们的差异,是本文的核心。实际上Q相比动多态, runtime unbound多态ؓ我们提供了更本质的运行时多态手D,我们可以从中获得更大的收益。但是鉴于一些技术上的困难,runtime unbound多态无法进入主世界。不q,׃新的~程技术的出现Q得这U更好的q行时多态Ş式可以同动多态一比高下?br> <h2>动多?nbsp;   <br></h2>     废话说Q让我们从一个老掉牙的案例开始吧Q编写一个绘囄序,囑Ş包括矩Ş、椭圆、三角Ş、多边Ş{等。图形从脚本Q比如xmlQ中dQ创建后保存在一个容器中备查。通过遍历容器执行囑Şl制?br>    p么个题目Q很单,也很熟悉Q解释OOP的动多态最常用的案例。下面我们就从动多态实现开始?br>    首先定义一个抽象基c,也就是接口:<br> <p>    class IShape</p> <p>    {</p> <p>        virtual void load(xml init)=0;</p> <p>        virtual void draw(monitor m)=0;</p> <p>        ...<br></p> <p>    };</p> <p>    然后定义各种囑Şc,q从q个接口上承:</p> <p>    class Rectangle: public IShape<br></p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor m) {...}</p> <p>        ...<br></p> <p>    };</p> <p>    class Ellipse: public IShape</p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor m) {...}</p> <p>        ...</p> <p>    };</p> <p>    ...</p> <p> </p> <p>    void DrawShapes(monitor m, vector<IShape*> const& g)</p> <p>    {</p> <p>        vector<IShape*>::const_iterator b(g.begin()), e(g.end());<br> </p> <p>        for(; b!=e; ++b)</p> <p>        {</p> <p>            (*b)->draw(m);<br> </p> <p>        }<br> </p> <p>    }<br> </p> <p>    ...<br></p> <p>    现在可以使用q些囑ŞcMQ?/p> <p>    vector<IShape*> vg;</p> <p>    vg.push_back(new Rectangle);</p> <p>    vg.push_back(new Ellipse);</p> <p>    ...</p> <p>    DrawShapes(crt, vg);<br></p> <p>    通过接口IShapeQ我们可以把不同的图形类l一CU类型下。但是,通过虚函数的overrideQ由囑Şcd现IShape上的虚函数。这可以? 生常谈了。动多态的核心是利用override和late bound的组合,使得一个基cd以在cd归一化的情况下,拥有l承cȝ语义。OOP设计模式大量q用q种技术,实现很多需要灵zL展的pȝ?/p> <h2>Runtime Unbound</h2>     Runtime Unbound多态؜合了静多态和动多态的特征Q即既有cd泛化Q又是运行时x的。一个最典型的例子就是ruby的函敎ͼ<br>    class x<br>       <strong>def fun(car)</strong><br style="font-weight: bold;"><strong>            car.aboard</strong><br style="font-weight: bold;"><strong>        end</strong><br>    end<br>    q个案例非常明确地展C出了Runtime Unbound多态的特点。car参数没有cdQ这里也不需要关心类型,只要求car对象有一个aboardҎ卛_。由于ruby是动态语aQ能够运行时对象的特征Qƈ动态调用对象上的方法?br>    在Runtime Unbound的思想指导下,我们利用一U伪造的“动态C++”Q把上面的绘图例子重新编写:<br> <p>    class Rectangle<br> </p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor dev) {...}</p> <p>        ...<br> </p> <p>    };</p> <p>    class Ellipse</p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor dev) {...}</p> <p>        ...</p> <p>    };</p> <p>    ...</p> <p>    void DrawShapes(monitor dev, vector<<strong>anything</strong>> const& g)</p> <p>    {</p> <p>        vector<IShape>::const_iterator b(g.begin()), e(g.end());<br> </p> <p>        for(; b!=e; ++b)</p> <p>        {</p> <p>            (*b).draw(dev);<br> </p> <p>        }<br> </p> <p>    }</p> <p>    ...<br></p> <p>    vector<<strong>anything</strong>> vg;</p> <p>    vg.push_back(Rectangle(...));</p> <p>    vg.push_back(Ellipse(...));</p> <p>    ...</p> <p>    DrawShapes(crt, vg);<br></p>     囑ŞcM再从抽象接口IShapel承Q而用关键字anything实例化vector<>模板。这个虚构的anything关键字所L? 用就是得vector能够接受不同cd的对象。当DrawShapes()函数接收到存攑֛形对象的容器后,遍历每一个对象,q且调用对象上的draw ()函数Q而不其cd?br>    从这D代码中Q我们可以看出Runtime Unbound多态带来的好处。所有图形类不再需要归一化成一个类型(抽象接口Q。每个类只需按照U定Q实现load、draw{成员函数即可。也是 _q些囑Şc解耦合了。一旦类型解耦,便赋予我们很大的自由度。最典型的情况就是,我们需要用一个其他h开发的囑Şc,q且无法修改其实现。此Ӟ? 果用动多态,很ȝ。因为尽这些图形类都拥有load、draw{函敎ͼ但毕竟不是承自IShapeQ无法直接插入容器。必ȝ写一个承自 IShape的适配器,作ؓ外来囑Şcȝ包装Q{发对其的讉K。表面上Q我们只是减一个接口的定义Q但Runtime Unbound多态带来的解耦有着非凡的意义。因为类耦合始终是OOP设计中的一个o人头痛的问题。在后面Q我们还看到徏立在Runtime Unbound多态基上的更大的进步?br>    然而,管Runtime Unbound多态具有这些优点,但因为徏立在动态语a之上Q其自n存在的一些缺陷得这Ҏ术无法广泛用,q进入主?br>    Runtime Unbound多态面临的W一个问题就是类型安全。确切的讲是静态类型安全?br>    本质上,Runtime Unbound多态(动态语aQƈ非没有类型安全。当动态语a试图讉K一个未知类型对象的成员Ӟ会通过一些特D机制或Ҏ接口获得cd信息Qƈ在其中寻 找所需的对象成员。如果没有找刎ͼ便会抛出异常。但是,传统上,我们希望语言能够在编译期得到cd安全保证Q而不要在q行时才发现问题。也是_ Runtime Unbound多态只能提供运行时cd安全Q而无法得到静态类型安全?br>    W二个问题是性能。Runtime Unbound需要在q行时搜ȝ型的接口Qƈ执行调用。执行这cd扑֒调用的方法有两种Q反和动态链接?br>    反射机制可以向程序提供类型的信息。通过q些信息QRuntime Unbound可以了解是否存在所需的接口函数。反通常也提供了接口函数调用的服务,允许参数打包,q过函数名调用。这U机制性能很差Q基本上无法用于E许密集些的操作?br>    动态链接则是在讉K对象前在对象的成员函数表上查询ƈ获得相应函数的地址Q填充到调用方的调用表中Q调用方通过调用表执行间接调用。这U机制相对快一些,但由于需要查询成员函数表Q复杂度基本上都在O(n)左右Q无法与动多态的O(1)调用相比?br>    q些问题的解冻I依赖于一U新兴的技术,即concept。concept不仅很消除了cd安全的问题,更主要的是它大幅~小了两URuntime多态的性能差距Q有望Runtime Unbound成ؓL的技术?br> <h2>concept</h2>     随着C++0x逐渐出水面Qconcept作ؓ此次标准更新的核心部分,已经在C++C中引起关注。随着旉的推U,concept的潜在作用也在不断被发掘出来?br>    concept主要用来描述一个类型的接口和特征。通俗地讲Qconcept描述了一l具备了共同接口的类型。在引入concept后,C++可以Ҏ板参数进行约束:<br>    concept assignable<T> {<br>        T& operator=(T const&);<br>    }<br>    template<assignable T> void copy(T& a, T const& b) {<br>        a=b;<br>    }<br>    q表C类型T必须有operator=的重载。如果一个类型X没有对operator=q行重蝲Q那么当调用copyӞ便会引发~译错误。这使得cd参数可以在函C用之前便能得到检验,而无需{到对象被用时?br>    另一斚wQconcept参与到特化中后,使得操作分派更加方便Q?br>    concept assignable<T> {<br>        T& operator=(T const&);<br>    }<br>    concept copyable<T> {<br>        T& T::copy(T const&);<br>    }<br>    template<assignable T> void copy(T& a, T const& b) {    //#1<br>         a=b;<br>     }<br>     template<copyable T> void copy(T& a, T const& b) {    //#2<br>        a.copy(b);<br>    }<br>    X x1,x2; //X支持operator=操作W?br>    Y y1,y2; //Y拥有copy成员函数<br>    copy(x1, x2);    //使用#1<br>    copy(y1, y2);    //使用#2<br>    在静多态中Qconcept很好地提供了cdU束。既然同hUnboundQ那么concept是否同样可以被用于Runtime UnboundQ应当说可以Q但不是现有的concept。在Runtime Unbound多态中Q需要运行时的concept?br>     依旧使用l图案例做一个演C。假设这里用的"C++"已经支持conceptQƈ且也支持了运行时的conceptQ?br> <p>    class Rectangle<br> </p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor dev) {...}</p> <p>        ...<br> </p> <p>    };</p> <p>    class Ellipse</p> <p>    {</p> <p>        void load(xml init) {...}</p> <p>        void draw(monitor dev) {...}</p> <p>        ...</p> <p>    };</p> <p>    ...</p> <p>    concept Shape<T> {</p> <p>        void T::load(xml init);</p> <p>        void T::draw(monitor dev);<br></p> <p>    }</p> <p>    ...<br></p> <p>    void DrawShapes(monitor dev, vector<<strong>Shape</strong>> const& g)</p> <p>    {</p> <p>        vector<IShape>::const_iterator b(g.begin()), e(g.end());<br> </p> <p>        for(; b!=e; ++b)</p> <p>        {</p> <p>            <strong>(*b).draw(dev);</strong><br> </p> <p>        }<br> </p> <p>    }</p> <p>    ...<br> </p> <p>    vector<<strong>Shape</strong>> vg;</p> <p>    vg.push_back(Rectangle(...));</p> <p>    vg.push_back(Ellipse(...));</p> <p>    vg.push_back(string("xxx"));    //错误Q不W合Shape concept<br></p> <p>    ...</p> <p>    DrawShapes(crt, vg);</p> <p>    乍看h没什么特别的Q但是请注意vector<Shape>。这?span style="font-weight: bold;">使用一个conceptQ而不是一个具体的cdQ实例化一个模?/span>。这里的意思是_q个容器接受的是所有符合Shape concept的对象,cd不同也没关系。当pushqvg的对象不W合ShapeQ便会发生编译错误?/p> <p>     但是Q最关键的东西不在这里。注意到DrawShapes函数了吗Q由于vector<Shape>中的元素cd可能完全不同。语? (*b).draw(dev);的语义在静态语a中是非法的,因ؓ我们Ҏ无法在编译时具体定(*b)的类型,从而链接正的draw成员。而在q里Q? ׃我们引入了Runtime UnboundQ对于对象的讉K链接发生在运行时。因此,我们便可以把不同cd的对象存攑֜一个容器中?/p> <p>    concept在这里vCcd验的作用Q不W合相应concept的对象是无法攑օq个容器的,从而在此后对对象的使用的时候,也不会发生类型失配的 问题。这也就在动态的机制下确保了cd安全。动多态确保类型安全依靠静态类型。也是所有类型都从一个抽象接口上l承Q从而将cd归一化,以获得徏立在? 态类型系l之上的cd安全。而concept的类型安全保证来源于对类型特征的描述Q是一U非侵入的接口约束,灉|性大大高于类型归一化的动多态?br></p> <p>    如果我们引入q样一个规则:如果用类型创建实例(对象Q,那么所创徏的对象是静态链接的Q也是~译旉接;而用concept创徏一个对象,那么所创徏的对象是动态链接的Q也是q行旉接?/p> <p>    在这条规则的作用下,下面q段单的代码会产生非常奇妙的效果:</p> <p>    class nShape</p> <p>    {</p> <p>    public:<br></p> <p>        nShape(<strong>Shape g</strong>, int n) : <strong>m_graph(g)</strong>, m_n(n) {}<br></p> <p>        void setShape(<strong>Shape g</strong>) {</p> <p>            <strong>m_graph=g;</strong><br></p> <p>        }<br></p> <p>    private:</p> <p>        <strong>Shape    m_graph;</strong></p> <p>        int        m_n;<br></p> <p>    };</p> <p>    在规则的作用下,m_graph是一?strong>动态对?/strong>Q它的类型只有在q行时才能明。但是无Z么类型,必须满Shape concept。而m_n的类型是定的,所以是一?strong>静态对?/strong>?/p> <p>    q和传统的模板有区别吗?模板也可以用不同的类型参数定义成员数据。请看如下代码:</p> <p>    Rectangle r;</p> <p>    Ellipse e;<br><strong>     nShape(r, 10);</strong></p> <p><strong>     nShape.setShape(e);   </strong>//对于传统模板而言Q这个操作是非法的,因ؓe和r不是同一U类?/p> <p>    动态对象的特点在于Q我们可以在对象创徏后,用一个不同类型的动态对象代替原来的Q只需要这些对象符合相应的concept。这在静态的模板上是做不到的?br></p>     现在回过头来看一下用concept实例化模板的情Ş。我们知道,用一个类型实例化一个模板,得到的是一个类Q或者说cd。而用一个concept实例? 一个模板,得到的又是什么呢Q还是一个concept。那么vector<Shape>是一个conceptQ因而它的实例是动态的对象。当 Ӟ实际上没有必要把vector<Shape>的实例整个地当成动态对象,它只是具有动态对象的行ؓ特征。在实现上,vector< Shape>可以按照普通模板展开Q而其内部由concept模板实参定义的对象作为动态对象处理即可?strong>一个由concept实例化的模板的对象作义上的动态对?/strong>?br>    下面的代码则引出了另一个重要的Ҏ:<br>    vector<<strong>float</strong>> vFloat;    //静态对象的容器Q内部存攄都是静态对象,属于同一cdfloat<br>    vector<<strong>Shape</strong>> vShape; //动态对象的容器Q内部存攑֊态对象,都符合Shape<br>    同一个类模板Q当<span style="font-weight: bold;">使用cd实例化,执行static unbound多?/span>Q?span style="font-weight: bold;">使用concept实例化,执行runtime unbound多?/span>。两者的形式相同。也是?strong>static多态同runtime多态以相同的Ş式表?/strong>? ׃concept的加入,两种完全不同的多态被l一在同一个模型和形式下。实际上Qstatic和runtime unbound多态可以看作同一个抽象体pȝ两个分支Q分别处理不同情늚应用。而Ş式上的统一Q则更加接近抽象体系的本质。同Ӟ也得两U? unbound多态的差异被后台化Q用者无需额外的工作,便可以同时获得动态和静态的抽象能力。同Ӟ两种多态所展示的逻辑上的对称性,也暗CZ两者在 本质上的联系。这里统一的Ş式,便是q种对称性的l果?br>    对于模板函数Q则会表现出更加有趣的特性(q个函数模板有些特别Q不需要template关键字和cd参数列表Q这是我伪造的。但׃concept的用,它本质上q是一个模板)Q?br>    void draw(<strong>Shape</strong> g);<br>    q个函数接受一个符合Shape的参数。如果我们用一个静态对象调用这个函敎ͼ<br>    Rectangle r;<br>    draw(r);<br>    那么Q就执行static unboundQ实例化成一个完完整整的函数Q同传统的函数模板一栗?br>    如果用一个动态对象调用这个函敎ͼ<br>    <strong>Shape g=Cycle();</strong><br>    draw(<strong>g</strong>);<br>    <strong>g=Rectangle();</strong><br>     draw(<strong>g</strong>);<br>    那么Q就执行runtime unboundQ生成一个等待运行时链接的函数。上面的两次调用Q分别进行了两次q行旉接,以匹配不同的动态对象?br>    q样Q我们可以通过函数调用时的参数对象Q来控制使用不同的多态Ş式。更复杂的情况就是用一个函数的q回D用另一个函敎ͼq样构成的调用链依然W合上述的调用控制原则?br>    下面Q我们将看到Runtime Unbound多态的一个精彩表演:<br>    //假设Q我们已l定义了Rectangle、Cycle、Square、Ellipse、Trangle五个c,<br>    // 分别map到Rectangles、Cycles、Squares、Ellipses、Trangles五个concept上,<br>    // q些concept都refineQ可以不正确地理解ؓl承吧)自Shape?br>    void draw(monitor dev, <strong>Rectangles</strong> r); //#3<br>    void draw(monitor dev, <strong>Cycles</strong> c);       //#4<br>    void draw(monitor dev, <strong>Squares</strong> s);    //#5<br>    void draw(monitor dev, <strong>Ellipses</strong> e);    //#6<br>    void draw(monitor dev, <strong>Trangles</strong> t);    //#7<br>    //此处定义一个Shape的动态对?br>    <strong>Shape</strong> g=<strong>CreateShapeByUserInput</strong>();    //q个函数Ҏ用户输入创徏囑Ş对象Q所以图形对象的cd只能到运行时从能定?br>    draw(crt, g);<br>    好了Q现在该调用哪个版本的drawQ根据用L输入来。换句话_调用哪个版本的drawQ取决于<strong>CreateShapeByUserInput</strong>()函数的返回结果,也就是用戯入的l果。如?strong>CreateShapeByUserInput</strong>() q回Rectangle的动态对象,那么执行#3Q如果返回的是Trangle对象Q那么执?7。这是一U动态分z操作。在q行时concept的作 用下Q实现v来非常容易。对draw的调用最l会被{换成一个concept需求表Q来自draw函数Q每一对应一个函数版本,q且指明了所对应? concept。动态对象上也有一个concept表,每一存放了q个对象所W合的concept。用q两个表怺匚wQ可以找到g对象? concept最匚w的那个draw版本Q然后调用?br>    q实际上是将重蝲x攑ֈq行时进行,而concept在其中vC匚w参数的作用?br>    q样的做法同利用rtti信息执行cd分派调用cMQ?br>    void draw_impl(monitor dev, Rectangle& r);<br>     void draw_impl(monitor dev, Cycle& c);<br>     void draw_impl(monitor dev, Square& s);<br>     void draw_impl(monitor dev, Ellipse& e);<br>     void draw_impl(monitor dev, Trangle& t);<br>    void draw_impl(monitor dev, Shape& g) {<br>        if(typeif(g)==typeid(Rectangle))<br>            draw_impl(dev, (Rectangle&)g);<br>        else if(typeif(g)==typeid(Cycle))<br>            draw_impl(dev, (Cycle&)g);<br>        ...<br>    }<br>    但是Q他们却有着天壤之别。首先,rtti分派是R入的。如果需要增加一个图形,需要在draw函数中增加分z代码。而Runtime UnboundҎ则只需要用新的concept重蝲draw函数卛_?br>    其次Qrtti版本有多图形类Q就需要多if...else...Q而Runtime Unbound则是一对多的。如果有几个囑Şcd容不同,但有相同的接口,W合同一个conceptQ那么只需针对concept~写一个函数版本即可? 比如Q如果有一个特别的CycleExc,使用外界正方形的左上?右下角坐标描qͼ正好W合Ellipses conceptQ那么只需CycleEx map到Ellipses上即可,无需多加M代码?br>    最后,rtti需要获取类型信息,然后做线性比较,性能无法优化。但Runtime Unbound通过concept表的怺匚wQ仅牉|数值操作,有很大的优化I间?br>    那么q样一U运行时分派有什么好处呢Q我们看到图形类上的draw函数接受一个monitorcd参数Q它代表讑֤。如果哪一天需要向另一U设备,比如 printerQ输出图形,那么需要在囑ŞcM增加另一个版本的draw函数。如果类是别人开发的Q那么就增加沟通的负担。如果类是外来的Q我们无法修 改,那么只能通过adapter之类的笨拙手D处理。ؓ了让monitor之类同图形本w没有关联的东西分离Q应当用自由函数执行draw操作。但普? 函数只能接受定的类型重载,而传l的函数模板则限于编译期使用Q无法进行运行时分派。所以,如果能够使用concept重蝲函数Qƈ且赋? Runtime UnboundQ那么便可以用最单的形式针对一cȝ型进行处理,效能高得多?br> <h2>q行时concept<br></h2> <p>   语言层面的concept无法做到q些Q因为它是编译期机制。ؓ此,我们需要有一U运行时的conceptQ或者说二进制别的concept?/p> <p>    一个concept包含了与一个或若干个类型有关的一l函敎ͼ包括成员函数和自由函数。于是,我们可以用一个类?#8220;虚表”的函数指针表Q暂且称? ctable吧)存放concept指定的函数指针。这Lctable依附在动态对象上Q就像vtable一栗每个对象都会匹配和map到若q个 concept。因此,每个动态对象会有一个concept表,其中存放着指向各ctable的指针,以及相应的concept基本信息?/p> <p>    当一?#8220;用户”Q函数或模板Q需要在q行旉接到对象上的时候,它会提交一个concept的代码(全局唯一Q。系l用q个代码在动态对象的 concept表上索,获得指向所需concept的指针,q且填写?#8220;用户”l出的一?#8220;插入?#8221;Q一个指针)中。随?#8220;用户”便可以直接通过q个 “插入?#8221;间接调用所需的函敎ͼ成员或自由函数?/p> <p>    在这里,concept的y妙之处在于,一族函数集合在一P作ؓ一个整体(x口)。那么,在执行运行时匚w的时候,不再是一个函C个函数地查询Q? 可以一ơ性地Lq些函数是否存在。这很Ҏ地规避了cd安全保证操作的损耗。如果用hash查询Q那么可以在O(1)实现concept匚w。另 外,一个concept的hash值可以在~译时计好Q运行时链接只需执行hash表检索,qhashD也可以省去?/p> <p>    一个动态对象可以直接用指向concept ctable的指针表C。在不同concept之间转换Q相当于改变指针的指向,q种操作非常cMOOP中的dynamic_cast?/p> <p>    对于如下的动态对象定义:</p> <p>    Shape g=Cycle();</p> <p>    会创Z个Cycle对象Q在对象上构v一个concept表,表中对应Cycle所有符合的concept。ƈ且徏立一lctableQ每? ctable对应一个concept。每个concept表项指向相应的ctable。而符号g则实际上是指向所建立对象的Shapes ctable的指针?/p> <p>    对于函数Q?/p> <p>    void draw(<strong>Shape</strong> g);</p> <p>    draw(g);<br></p> <p>    调用gӞ׃draw的参数是Shape conceptQ而g正是draw所需的conceptQ所以无需在对象g的concept表上匚wQ可以直接用这个ctable指针。这是_只要 所用动态对象(gQ的concept同用方Qdraw函数Q能够匹配,便可以直接用指向ctable的指针链接(~译旉接)Q无需在运行时重新? 配。只有发生concept转换Ӟ才需要在concept表中搜烦Q获得所需的ctable指针Q?/p> <p>    Swappable s=g; //Swappable是另一个concept<br></p> <p>    q种情况同dynamic_cast极其怼。也可以模仿着采用concept_cast之类的操作符Q得concept转换昑ּ化,消除隐式转换的问题(强concept化)?br></p>     所以,Runtime Unbound在运行时concept的作用下Q具有同动多态相同的底层行ؓ。因而,他们的性能也是一L。很多用于动多态的Ҏ和算法都可以直接用于q行时concept?br> <h2>Runtime Unbound和Runtime Bound</h2>     对于runtime unbound同runtime bound之间的差异前面已l有所展示。在其他斚wQ两者还存在更多的差别?br>    首先Q就像绘图案例中展示的那Pruntime unbound是非侵入的。runtime unbound不要求类型承自同一cdQ只需类型同concept兌h便可?br>    其次Qconcept不是一U局限于OO的技术,不仅涉及成员函数Q还包括了自由函敎ͼ范围更广Q更加灵zR?br>    最后,实现上,Runtime Unbound和Runtime Bound之间有惊人的怼之处。两者都采用一个函数指针表作ؓ操作分派Q都采用一个指向函数表的指针作为入口;一个动态对象上的concept之间的{ 换,也同动多态对象一P在不同的函数表间切换。他们唯一的不同,是实现接口的机制?br>    动多态用cdgQ接口Q通过l承和虚函数实现接口的功能。用cd作ؓcd的接口,使得q两个本来独立的概念交织在一赗增加整个类型体pȝ复杂度和耦合度?nbsp;   concept则利用独立的pȝ描述、表辑֒理接口。类型则回归到表达业务对象的功能上来?br>    动多态在使用cd表达接口的时候,便很Ҏ地引入一个麻烦的问题Q表辑֊能的cd和表达接口的cd混合在一P使用时必通过一些方法区分出哪些是接口, 哪些是功能类型。这增加了对象模型的复杂性。而concept则独立于cd体系之外Q所有对接口的操作都是单一的,索和匚w来得更加方便快捷?br>    作ؓl承体系的基部分Q动多态的抽象接口必须在承结构的最端。那么这些抽象类型必d于其他类型出现。这对系l的早期设计产生很大的压力,往往一个基抽象接口设计有误Q便会造成整个体系的变更?br>    而concept是独立于cd的,那么M时候都可以一个类型同接口l定。接口甚臛_以在cd体系基本建立之后才确定。这U灵zL对复杂软g的开发至关重要,L了长期以来套在h们头上的枷锁?br>    前面已经提到Q在不需要concept转换的情况下Q无需执行q行时的concept匚wQ所有的调用h同动多态一L效率Q都是间接调用)。在执行 concept转换Ӟ无需象动多态那样在复杂的承体pM索,只需执行concept表的hash匚wQ效率反而更高,而且更简单。考虑到这些情况, 我们可以认ؓ<strong>concept化的Runtime Unbound多态完全能够替代传l的动多?/strong>。也是_我们<strong>不再需要动多态了</strong>?br>     惌一下,如果一门语a能够拥有q行时conceptQ那么它完全可以只保留Static Unbound和Runtime Unbound多态,而放弃Runtime Bound多态。一旦放弃动多?没有了虚函数和虚?Q那么对象模型便可以大大化。所有对象只需要线性分布,基类和成员依ơ堆叠在一P也没? vtable的干扎ͼ对象l构可以做到最单。同Ӟl承也回归了代码重用的传l用途。而且Q对象独立于接口存储Q在能够在编译时静态链接的时候,可以? 为静态对象用。而在需要动态对象的地方Q又可以很容易地转换成动态对象,只需要ؓ光上concept表和ctable。一切都化了。对象模型也更加 Ҏl一?br>     q对于很多底层开发的E序员对于c++复杂而又混ؕ的对象模型难以接受。如果能够废除虚函数Q简化对象模型,那么对于q些底层开发而言Q将会带来直接的? 处。只要确保不使用concpt定义对象、实例化模板Q便可以使整个Y件执行Static Unbound。这相当于去掉OOP的C++。否则,启用Runtime UnboundQ实现运行时多态?br> <h2>ȝ</h2>     Static Unbound和Runtime Unbound作ؓ一对亲密无间的多态技术,体现了最完善的抽象Ş式。两者各t一方,怺补充Q相互支援。而且两者具有统一的表现Ş式,大大方便了用, 对于软g工程h非凡的意义。另一斚wQRuntime Bound多态作为OO时代的物,体现了静态类型语a在运行时多态方面的最大努力。但是,随着q行时concept的引入,Runtime Unbound多态自w存在的静态类型安全问题和性能问题Q都能够得到很好的解冟뀂至此,Runtime Unbound便具备了替代Runtime Bound的实力。相信在不久的将来,Runtime Bound会逐渐步入它的黄昏?br> <h2>参?/h2> <ol> <li>http://groups.google.com/group/pongba/web/Runtime+Polymorphic+Generic +Programming.pdf。大牛hJaakko Järvi{写的关于Runtime concept的文章,讲解了runtime concept的概늚实现ҎQƈ在ConceptC++上以库的形式实现。其中用传l的动多态实现runtime conceptQ这表明动多态的实现机制同runtime concept是一致的。当然库的实现很复杂Q这?#8220;壳里做道?#8221;Q无奈之举。Runtime conceptq是应当在语a中first-class地实现?/li> <li>http://www.lubomir.org/academic/MinimizingCodeBloat.pdf。也是Jaakko Järvi写的Q运行时分派的文章?/li> <li>http://opensource.adobe.com/wiki/index.php/Runtime_Concepts?/li> <li>Inside C++ Object Model?br> </li> </ol> <h2>附录 Runtime Concept的具体实?/h2>     我们有一个conceptQ?br>    concept Shape<T><br>    {<br>        void T::load(xml);<br>        void T::draw(device);<br>        void move(T&);<br>    }<br>    另外Q还有一个代表圆的conceptQ?br>    concept Cycles<T> : <br>        CopyConstructable<T>,<br>        Assignable<T>,<br>        Swappable<T>,<br>        Shape<T><br>    {<br>         T::T(double, double, double);<br>        double T::getX();<br>         double T::getY();<br>         double T::getR();<br>         void T::setX(double);<br>         void T::setY(double);<br>         void T::setR(double);<br>     }<br>    现在有类型CycleQ?br>     class Cycle<br>     {<br>     public:<br>         Cycle(double x, double y, double r);<br>         Cycle(Cycle const& c);<br>         Cycle& operator=(Cycle const& c);<br>         void swap(Cycle const& c);<br>         void load(xml init);<br>         void draw(device dev);<br>         double getX();<br>         double getY();<br>         double getR();<br>         void setX(double x);<br>         void setY(double y);<br>         void setR(double r);<br>     private:<br>         ...<br>     };<br>     当定义一个动态对象:<br>     Shape g=Cycle();<br>     便会形成如下囄l构Q?br> <div class="xzrzddr" id="w08n" style="padding: 1em 0pt; text-align: left;"> <div class="dvdbllv" id="ny7x" style="padding: 1em 0pt; text-align: left;"><img alt="" src="http://www.shnenglu.com/images/cppblog_com/longshanks/runtime_concept.JPG" height="338" width="648"><br> </div> </div>     g实际上是一个指针,指向concept表的Shape,而ShapeҎ向Shape对应的ctable。由于Cycle refine自Shape{众多conceptQ那么Cycle的ctable实际上包含了q些concept的ctableQ所以只需一个Cycle? ctableQ而其他concept都分别指向其中各自相应的部分。ctable中的每一个项则指向具体的函数体?br>     如果遇到语句Q?br>     Swappable h=concept_cast<Swappable>(g);<br>     那么Q将会执行一个搜索,用concept Swappable的idQ比如hash码)在concept表中索是否存在SwappableV如果存在,将对应的指针赋给h。这U操作同 dynamic_cast操作非常怼Q只是相比在复杂的对象结构中查询更加单迅速?br>     concept表置于对象的头部或尾部,q是Z便于对象索concept接口。每个类型的ctable只需一份?br>     对象本体可以很容易地同concept表分,在完全静态的情况下,concept表是不需要的。如果需要runtime多态,加上concept表即可?br> <br> <img src ="http://www.shnenglu.com/longshanks/aggbug/37915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/longshanks/" target="_blank">longshanks</a> 2007-12-06 17:20 <a href="http://www.shnenglu.com/longshanks/archive/2007/12/06/37915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.dmchs.cn" target="_blank">Ұ¾þһ</a>| <a href="http://www.pc168.com.cn" target="_blank">ݺۺϾþۺ88</a>| <a href="http://www.yksxc.cn" target="_blank">ľþþƷww16</a>| <a href="http://www.odcb.cn" target="_blank">þþ뾫Ʒպ˳</a>| <a href="http://www.lc351.cn" target="_blank">뾫Ʒþþþ..</a>| <a href="http://www.2pt.com.cn" target="_blank">޹Ʒþ</a>| <a href="http://www.1000su.cn" target="_blank">þAVĻ</a>| <a href="http://www.animin.cn" target="_blank">þֻƷ99</a>| <a href="http://www.77gly.cn" target="_blank">ŷձþùʵҶ԰</a>| <a href="http://www.trgba.cn" target="_blank">aѹۿþav</a>| <a href="http://www.airesou.cn" target="_blank">һһþþƷۺ</a>| <a href="http://www.5678121.cn" target="_blank">þþƷAV㽶</a>| <a href="http://www.kou365.cn" target="_blank">99þþƷѾƷ</a>| <a href="http://www.yangyongfu.com.cn" target="_blank">Ʒþˬ</a>| <a href="http://www.grayhound.cn" target="_blank">þù˾Ʒ鶹</a>| <a href="http://www.waihuimaoyi.cn" target="_blank">þþƷAVDz18</a>| <a href="http://www.hfoa.com.cn" target="_blank">˹ھƷþþþӰԺVR</a>| <a href="http://www.7-go.cn" target="_blank">պƷþĻ</a>| <a href="http://www.vcexpress.cn" target="_blank">þwww˳ɾƷ㽶</a>| <a href="http://www.91share.com.cn" target="_blank">þþþþAvӰԺ </a>| <a href="http://www.xyjsj88.com.cn" target="_blank">þþþþþþŮ</a>| <a href="http://www.satyw.cn" target="_blank">þþƷav鶹ɫ </a>| <a href="http://www.hhh328.cn" target="_blank">۲ӰԺþþƷ</a>| <a href="http://www.h6343.cn" target="_blank">պavþþƷ</a>| <a href="http://www.ekqt.cn" target="_blank">ɫۺϾþ</a>| <a href="http://www.fz-tm.cn" target="_blank">þþþþavѿƬ</a>| <a href="http://www.odostudio.cn" target="_blank">þ99Ʒþ99ý </a>| <a href="http://www.xnhtml.com.cn" target="_blank">ĻѾþ</a>| <a href="http://www.zqyipin.cn" target="_blank">һaƬþëƬ</a>| <a href="http://www.bushenba.cn" target="_blank">պŷþþwwwۺ</a>| <a href="http://www.ha9hpn.cn" target="_blank">ٸ88þĻ</a>| <a href="http://www.ihi7113575.cn" target="_blank">þҹƵ</a>| <a href="http://www.oubaidu.cn" target="_blank">ľƷþþþ</a>| <a href="http://www.sbsinc.com.cn" target="_blank">þˬˬƬAV</a>| <a href="http://www.gljqk.cn" target="_blank">ƷһþþƷ</a>| <a href="http://www.101922.cn" target="_blank">þwwƷw˳</a>| <a href="http://www.gjvthsj.cn" target="_blank">þþþһƷɫ</a>| <a href="http://www.okboom.cn" target="_blank">þþƷav鶹С˵</a>| <a href="http://www.oahk.cn" target="_blank">Ʒ9999þþþ</a>| <a href="http://www.wyj448.cn" target="_blank">91þþƷֱ</a>| <a href="http://www.sifubaimeng.cn" target="_blank">aaþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>