sizeof 感知重载,模板具现, 转换规则

来源:互联网 发布:python hdfs 数据读取 编辑:程序博客网 时间:2024/05/22 13:06

问题:如何侦知任意型别 T 是否可以自动转换为型别 U?
 
方案:侦测转换能力的想法:合并运用 sizeof 和重载函数。
 
1 依赖 sizeof,sizeof 有着惊人的能力,你可以把 sizeof  用在任何表达式身上,不论后者有多复杂。sizeof 会直接传回大小,不需拖到执行期才评估。这意味着 sizeof 可以感知重载 (overloading)、模板具现(template instantiation)、转换规则(conversion rules)、或任何可发生于C++ 表达式身上的机制。
 
sizeof  的其他用法可参考:http://blog.csdn.net/renwotao2009/article/details/40478607
 
2 提供两个重载函数:其中一个接受 U(U 型别代表目前讨论中的转换目标)。另一个接受 ”任何其他型别“。以型别 T 的暂时对象来调用重载函数,T 是否可转换成 U 正式我们要验证的。如果接受 U的那个函数被调用,我们就知道 T 可转换为 U;否则 T便无法转换为 U。为了知道哪一个函数被调用,两个重载函数的返回不同的返回型别,并以 sizeof 来区分其大小。型别本身无关紧要,重要的是其大小必须不同。
 

 

[cpp] view plain copy
01.// 最简单区分返回值大小的方法 
02.typedef char Small; 
03.class Big { char dummy[2]; };  
04.// 声明两个重载函数  
05.Small Test(U); 
06.Big Test(...); // 调用一个带省略符的函数,可接受任何类型的参数,编译器优先选择最准确的重载函数,所以这当然是最差的选择 
07. 
08.// 这里调用 T() 的默认构造函数 
09.const bool convExists = sizeof (Test(T())) == sizeof(Small); // sizeof 会在编译器求得函数返回值的大小,但不会执行该函数体 
10.// 如果 T 类型的默认构造为 private,可以构造一个 T MakeT();  
11.const bool convExists = sizeof (Test(MakeT())) == sizeof(Small); 


当然,我们可以用 class template 包装,隐藏 型别推到的细节:

 


[cpp] view plain copy
01.template <class T, class U> 
02.class Conversion 
03.{ 
04.    typedef char Small; 
05.    class Big { char dummy[2]; }; 
06.    static Small Test(U);         // 很显然这里只能定义静态成员函数,否则下面 enum中 existes表达式中 sizeof 只能评估调用静态的成员函数,而普通的成员函数的调用依赖于类的实例对象。 
07.    static Big Test(...); 
08.    static T MakeT(); 
09.public: 
10.        // exists 值为 true 时,T 可转换为类型 U 
11.    enum { exists = sizeof(Test(MakeT())) == sizeof(Small)}; 
12.        // exist2Way 表示 T 和 U 之间是否可以双向转换。例如 int和 double 可以双向转换 
13.    enum { exists2Way = exists && Conversion<U, T>::exists }; 
14.        // 如果 T 和 U 是相同的型别, 这个值便为true 
15.    enum { sameType = false}; 
16.}; 
17.可以通过偏特化来实现sameType 
18.template <class  T> 
19.class Conversion < T, T > 
20.{ 
21.public: 
22.    enum { exists = 1, exists2Way = 1, sameType = 1 }; 
23.}; 


有了 Conversion 的帮助,可以定义宏来简化判断继承关系:

 

[cpp] view plain copy
01.#define SUPERSUBCLASS(T, U) \ 
02.    (Conversion<const U*, const T*>::exists && \ 
03.    !Conversion<const T*, const void*>::sameType) 


如果 U 是public 继承于 T,或 T 或 U 是同一型别,那么 SUPERSUBCLASS(T, U) 会传回 true。

当 SUPERSUBCLASS(T, U) 对 const U* 和 const T* 作“可转换”评估时,只有三种情况下 const U* 可以隐式转换为 const T*:
 
1 T 和 U是同一种型别
 
2 T 是 U 的一个 unambiguous (不模棱两可的、非歧义的) public base。
 
3 T 是 void。
 
第三种情况可以再第二次测试中解决掉。更严谨的测试:
 

 

[cpp] view plain copy
01.#define SUPERSUBCLASS_STRICT(T, U) \ 
02.    (SUPERSUBCLASS(T, U) && \ 
03.    !Conversion<const T, const U>::sameType) 


为何这些代码都加上 const 修饰?原因是我们不希望因 const 而导致转型失败。 如果 template 代码实施 const 两次(对一个已经是 const 的型别而言),第二个 const 会被忽略。在 SUPERSUBCLASS 中使用 const,更安全些。
 
SUPERSUBCLASS 很明显告诉使用者 判断 T 是否是 U 的父类。
 

测试:
 

 

[cpp] view plain copy
01.class super{}; 
02.class sub :public super {}; 
03.int main() 
04.{ 
05.    cout << Conversion<double, int>::exists << ' ' 
06.        << Conversion<char, char*>::exists << ' ' 
07.        << Conversion<size_t, vector<int> >::exists << endl; 
08.    bool bflag = SUPERSUBCLASS(super, sub); 
09.    bflag = SUPERSUBCLASS_STRICT(super, sub); 
10. 
11.return 0; 

0 0
原创粉丝点击