分享
 
 
 

《Modern C++ Design》源码Loki库读解随感二:类型间耦合检测和去耦合

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Loki库读解随感二:类型间耦合检测和去耦合

过了如许之久才有这随感二,实在不好意思。原因是我虽然读懂了Loki的每一行代码,却实在未能理解如何去使用这些代码,直到近来才渐渐有所悟的。

数据类型之间的联系主要有两类:一,类型之间存在着自动转换关系;二,类型间存在着继承关系,虽然它其实也表明了某种转换(主要是对象切片和指针向上映射)。

那么,如何判断类型间存在转换或继承呢?Loki库TypeManip提供了很精彩很完美的解决方法(注:《More Exceptional C++》Item 4也给出了讲解,可以参考。):

//WQ注:和以前讨论过的TypeList一样,Conversion类也是供编译期使用

//的,所以靠提供萃取来解决问题的。

template <class T, class U>

struct Conversion

{

typedef Private::ConversionHelper<T, U> H;

#ifndef __MWERKS__

//WQ注:这是精妙所在!讲解太长,放到后面进行。

enum { exists = sizeof(typename H::Small) == sizeof(H::Test(H::MakeT())) };

#else

enum { exists = false };

#endif

enum { exists2Way = exists && Conversion<U, T>::exists };

enum { sameType = false };

};

//WQ注:用偏特化解决相同类型

template <class T>

struct Conversion<T, T>

{

enum { exists = 1, exists2Way = 1,sameType = 1 };

};

// WQ注:用偏特化和特化解决void

template <class T>

struct Conversion<void, T>

{

enum { exists = 1, exists2Way = 0,sameType = 0 };

};

template <class T>

struct Conversion<T, void>

{

enum { exists = 1, exists2Way = 0,sameType = 0 };

};

template <>

class Conversion<void, void>

{

public:

enum { exists = 1, exists2Way = 1,sameType = 1 };

};

}

先看辅助类ConversionHelper的定义:

template <class T, class U>

struct ConversionHelper

{

typedef char Small;

struct Big { char dummy[2]; };

//WQ注:注意,下面三个函数并没有实现体!

static Big Test(...);//WQ注:C++中,不定参数已不需要“至少一个定参”了。

static Small Test(U);

static T MakeT();

};

看其中的Test函数,如果T到U之间存在转换关系的话,根据重载决策,肯定调Big Test(U)函数。依靠两个函数的返回类型并不相同,就可以判断出调了那个版本,于是也就推测出T到U之间是否存在转换关系了。

回过头来再看前面的使用:sizeof(typename H::Small) == sizeof(H::Test(H::MakeT()));由于我们讲过,Conversion是编译期使用的类,不想采用对返回值采用运行期比较运算,恰好有sizeof()运算满足要求。所以,ConversionHelper将两个Test函数的返回类型实现得在大小上不等。

令人崩溃的事来了:由于我们只使用了Big和Small的大小而没有用它的值,所以,可以根本不用真的创建返回对象,Test函数也不必真的被调用,于是所有行为都是编译期的了。C++说,你可以不用创建并没有被真的使用的东西,所以,Test函数可以不用被定义,只要有这个申明就行了。是的,Loki库中确实没有定义这三个函数!崩溃啊,崩溃!在没搞懂这一点之前,我反复搜索了全部源码,并认为我下载了一个不完全的版本,然后四处搜索最新版本,着实乱了好一阵。虽然我不敢说所有C++编译器都能支持这一点,但我使用的VC、BCB、DevCPP三个主流编译器都正确支持了这一点,Loki的源码能够正确编译和使用。

如何判断两个类型间存在继承关系?很简单:派生类指针类型可以自动向上映射为基类指针类型。当然还得排除“同类型指针间”和“非void *与void *间”这两种情况。Loki库源码如下:

////////////////////////////////////////////////////////////////////////////////

// macro SUPERSUBCLASS

// Invocation: SUPERSUBCLASS(B, D) where B and D are types.

// Returns true if B is a public base of D, or if B and D are aliases of the

// same type.

//

// Caveat: might not work if T and U are in a private inheritance hierarchy.

////////////////////////////////////////////////////////////////////////////////

#define SUPERSUBCLASS(T, U)

(::Loki::Conversion<const U*, const T*>::exists &&

!::Loki::Conversion<const T*, const void*>::sameType)

////////////////////////////////////////////////////////////////////////////////

// macro SUPERSUBCLASS_STRICT

// Invocation: SUPERSUBCLASS_STRICT(B, D) where B and D are types.

// Returns true if B is a public base of D.

//

// Caveat: might not work if T and U are in a private inheritance hierarchy.

////////////////////////////////////////////////////////////////////////////////

#define SUPERSUBCLASS_STRICT(T, U)

(SUPERSUBCLASS(T, U) &&

!::Loki::Conversion<const T, const U>::sameType)//WQ注:有人指出此处最好使用const T *,const U *,但确实T和U就足够了,只是编译耗时可能要多一点点,反正运行期都是没有任何开销。

故事还没有结束,提供的萃取值是int的0或1(当然也可以认为等价于bool的false或true),它们只能作运行期运算的,那么如何编译期使用这些结果?Loki库同样有精彩的解决方法:

////////////////////////////////////////////////////////////////////////////////

// class template Int2Type

// Converts each integral constant into a unique type

// Invocation: Int2Type<v> where v is a compile-time constant integral

// Defines 'value', an enum that evaluates to v

////////////////////////////////////////////////////////////////////////////////

template <int v>

struct Int2Type

{

enum { value = v };

};

////////////////////////////////////////////////////////////////////////////////

// class template Select

// Selects one of two types based upon a boolean constant

// Invocation: Select<flag, T, U>::Result

// where:

// flag is a compile-time boolean constant

// T and U are types

// Result evaluates to T if flag is true, and to U otherwise.

////////////////////////////////////////////////////////////////////////////////

template <bool flag, typename T, typename U>

struct Select

{

typedef T Result;

};

template <typename T, typename U>

struct Select<false, T, U>

{

typedef U Result;

};

Select,一个编译期的?:运算。编译期只能靠类型推导(模板),类型判决(重载)来选择不同的分支。Int的0和1自然不在这两种情况之列,但Int2Type<0>和Int2Type<1>当然是满足的。依靠偏特化,Select圆满完成了它的任务。

类型之间有自动转换当然是极有用的,但很多时候却也造成麻烦:一,很容易产生转换多径,尤以多继承时最为麻烦;二,发生期望外的转换而得到期望外的行为,尤以重载时的情况最为混乱;三,转换不够精确,不能完全满足需要,问题发生在向下类型映射时。

在适当的时候去除了类型间耦合就可以避免这三方面副作用。

类型间去耦合有两种方法:一,用模板封装;二,指向指针的指针。

方法一的实现太简单了:

template<typename T>

struct Type2Type

{

typedef T OriginalType;

}

多个T之间的转换和继承关系在Type2Type<T>之间再不存在了。更为神奇的是,向下类型映射将可以精确进行的,但这得结合其具体使用来讲,我把它留到下一篇《多继承的改良》中讲。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有