c++ compile-time 检测两个类型之间是否可以转化

    在学习《modern c++ design》的时候,学习到了如何在 compile-time 检测两个类型之间是否可以转化。

    这里的转换,既包括 int,long,double 这些数据类型之间的转换,也包括基类和子类之间的转换(也就是两个类之前是否存在 class A 继承自 class B)。但是这篇文章最核心的点在于 compile-time 这个限定词。因为这些检测是在 compile-time 做出的,所以要比使用 dynamic_cast 的效率高很多(后者在 runtime 有较大的开销,应该尽量避免)。

    书中的解法是使用了 sizeof 这个关键字。提到 sizeof,忍不住想要先提一下这个关键字的妙用:你可以对任意复杂的表达式使用 sizeof 关键字,最关键的一点是,sizeof 后面的表达式并不会 runtime 被执行。也就是说 sizeof(expr) 的值,在 compile-time 就已经确定下来。下面有代码佐证:

#include <stdio.h>

int test();

int test2() {
  printf(__func__);
  return 1;
}

int main() {
  void(sizeof(test()));
  void(sizeof(test2()));
}

    上面代码可以正常的通过编译,运行后并没有任何输出,也就是没有任何函数调用。可以看到,test 函数甚至没有定义,这个性质在之后的实现中会被使用到。sizeof 的故事到此为止,下面让咱们说回正题。

    实现检测两个类型是否可转换使用的主要用到的技术是函数的重载和函数匹配时的类型之间的转换。其利用的主要是这样的一个事实:对于函数

int f(Base); char f(...);

    f(...) 代表最低等级的转换等级,其函数匹配的优先级最低,如果 class A 可以转化为 class Base,那么

f(A()); 

    应该是调用第一个版本,否则则为第二个版本。

    细心的朋友想必已经注意到,这两个函数的重载,不但重载了参数,还重载了返回类型,正是这个返回类型,搭配上前面的 sizeof 关键字,为我们这篇文章提供了一个完美的解决方案。

    下面贴出代码

template<typename T, typename U>
class Conversion {
private:
  static char Test(U);
  static int Test(...);
  static T MakeT();
public:
  enum { exists = sizeof(Test(MakeT())) == sizeof(char) };
};

    上面的代码有很多熟悉的面孔,包括最主要的函数重载和 sizeof 的使用。一个新面孔是 MakeT() 函数。这个是用来干嘛的呢?

    其实这个函数主要是为了兼容对于 T 的构造函数non-public 的情况。试想,如果将 MakeT() 调用替换为简单的 T(),那么上面的代码在遇到 T 类型的构造函数为 non-public 时,就无法奏效了,但是显然,我们并不关心这个类型的构造函数可不可以由客户端直接调用。引入了一个返回类型为 T 的函数以后,就巧妙的解决了这一点。

    短短不到10行代码,我们便实现了在 compile-time 检测两个类型之间是否可以转化,而这个检测的开销基本为0,你不需要做任何函数的调用,没有任何try...catch,没有任何对象的拷贝析构。c++ 模板编程的妙用,真的让人赞叹。下面是最后的测试代码:

#include "conversion.h"
#include <iostream>

struct B { };
struct D : public B { };
struct T { };

int main() {
  using namespace std;
  cout << Conversion<int, double>::exists << endl;
  cout << Conversion<int, int>::exists << endl;
  cout << Conversion<int, B>::exists << endl;
  cout << Conversion<D, B>::exists << endl;
  cout << Conversion<T, B>::exists << endl;
}

    输出为:

1
1
0
1
0
    与我们的期望完全一致。

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值