问题:如何侦知任意型别 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;

转自:https://blog.csdn.net/shenya1314/article/details/53975598

sizeof 感知重载,模板具现, 转换规则的更多相关文章

  1. 置入式模型inclusion model和显示具现化

    1.置入式模型 链接错误: 大多数非模板程序代码的组织如下:A,类声明在头文件中: B:全局变量和非inline函数在cpp文件中定义 但是,如果模板程序也这样组织,则会出错.原因在于:函数模板的定义 ...

  2. C++运算符重载 模板友元 new delete ++ = +=

    今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢. 一.类模板中的友元重载 本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写 ...

  3. C++ primer(八)--内联函数 引用变量 引用传递函数参数 函数重载/模板/模板具体化

    一.内联函数     常规函数和内联函数的区别在于C++编译器如何将他们组合到程序中.编译过程的最终产品是可执行程序--由一组机器语言指令组成.运行程序时,操作系统将这些指令载入到计算机内存中,因此每 ...

  4. C++内联函数、函数模板之于头文件

    一.基本说明 C++标准中提到,一个编译单元是指一个.cpp文件以及它所include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件 ...

  5. Effective C++ —— 模板与泛型编程(七)

    C++ templates的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates,他们发现templates有能力完成愈多可能的变化 ...

  6. c++模板类

    c++模板类 理解编译器的编译模板过程 如何组织编写模板程序 前言常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”.看看我们几乎每天都能遇到的模板类吧,如S ...

  7. C++-模板的声明和实现为何要放在头文件中

    源: http://blog.csdn.net/lqk1985/archive/2008/10/24/3136364.aspx 如何组织编写模板程序 发表日期: 1/21/2003 12:28:58 ...

  8. c++模板注意事项

    c++模板类 分类: C++2012-08-20 21:28 7108人阅读 评论(2) 收藏 举报 c++编译器instantiationiostreamlinker编程 c++模板类 分类: 数据 ...

  9. chap1 C++泛型技术基础--模板 #STL

    0 缘起 有一点编程经验和积累,想系统的学习下STL,以前都是随意做的笔记,现在想着成主题的输出一下. 书的原型是ISBN:9787302421757 <C++泛型STL原理和应用>,是从 ...

随机推荐

  1. bzoj3995

    线段树 额 计蒜客竟然把这个出成noip模拟题... 这个东西很像1018,只不过维护的东西不太一样 然后我参考了fuxey大神的代码,盗一波图 具体有这五种情况,合并请看代码,自己写了一个结果wa了 ...

  2. git根据commit生成patch(转载)

    转自:http://smilejay.com/2012/08/generate-a-patch-from-a-commit/ 在看一个Bugzilla上Xen的一个bug时,提到要revert掉Dom ...

  3. Math对象常用方法(取整细节)

    Math 对象 Math 对象用于执行数学任务. 1.常用属性: 1.E :返回算术常量e,即自然对数的底数(约2.718) 2.PI :返回圆周率,约3.14159 2.常用方法    Math.方 ...

  4. 洛谷 P3952 时间复杂度【模拟】

    把No写成NO,WA了一发-- 现在看这题也不难-- 用一个栈,记一下前面F的字母,是否合法,合法的有多长,每次入栈弹栈即可 #include<iostream> #include< ...

  5. 洛谷 P1083 借教室【二分+差分/线段树】

    二分mid,然后用1~mid的操作在差分序列上加减,最后把差分序列前缀和起来,看是否有有超过初始r值的 #include<iostream> #include<cstdio> ...

  6. 采购发票检验MIRO差异科目设置

    采购订单发票检验时,最终的金额可能跟采购订单的价格不一样,对于这部分差异,系统提供了后台配置科目的方式. 配置科目可通过OBYC,在BSX存货差异配置相关评估类型对应科目. 当库存商品少于采购订单数量 ...

  7. ios开发-常见的项目文件介绍

    一.项目文件结构示意图 二.文件介绍 1.products文件夹:主要用于mac电脑开发的可执行文件,ios开发用不到这个文件 2.frameworks文件夹主要用来放依赖的框架 3.test文件夹是 ...

  8. 洛谷 P1430 序列取数

    如果按照http://www.cnblogs.com/hehe54321/p/loj-1031.html的$O(n^3)$做法去做的话是会T掉的,但是实际上那个做法有优化的空间. 所有操作可以分解为由 ...

  9. 题解报告:hdu 1789 Doing Homework again(贪心)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1789 Problem Description Ignatius has just come back ...

  10. Oracle用户角色权限相关视图

    常用相关视图概述 DBA_SYS_PRIVS: 查询某个用户所拥有的系统权限 USER_SYS_PRIVS: 当前用户所拥有的系统权限 SESSION_PRIVS: 当前用户所拥有的全部权限 ROLE ...