美文网首页
C++11 模板元编程 - 模式匹配

C++11 模板元编程 - 模式匹配

作者: MagicBowen | 来源:发表于2016-09-15 22:50 被阅读869次

C++模板元编程中,编译器对模板的特化版本选择相当于是在做模式匹配,这个我们已经比较熟悉了。下面我们借助这一特性实现一个在模板元编程中最常使用的基础元函数IfThenElse,使用它可以完成类型选择的功能。

template<typename Condition, typename Then, typename Else> struct IfThenElse;

template<typename Then, typename Else>
struct IfThenElse<TrueType, Then, Else>
{
    using Result = Then;
};

template<typename Then, typename Else>
struct IfThenElse<FalseType, Then, Else>
{
    using Result = Else;
};

#define __if(...) typename IfThenElse<__VA_ARGS__>::Result

有了IfThenElse,就可以轻易地完成根据条件进行类型选择的计算。如下我们借助IfThenElse实现了一个元函数LargerType,它能够返回两个类型中内存空间更大的那个。

template<typename T, typename U>
using LargerType = __if(__bool(sizeof(T) > sizeof(U)), T, U);

除了模板特化,还有一个工具可以用来在模板元编程中完成模式匹配的功能,那就是C++编译器对重载函数的选择。

我们通过下面的示例展示如何通过函数重载来完成模式匹配。

我们知道C++中某些类型之间支持默认转型。例如short默认可以向int转型,子类指针可以默认转型为父类指针,而任何指针类型都可以默认转型为void*类型。下面我们将实现一个元函数,它能帮我们识别一个类型是否能够向另一个类型默认转型。

通过分析,我们定义这个元函数的的原型为:

IsConvertible :: (typename T -> typename U) -> BoolType

它的入参是两个类型T和U,如果T可以默认转型为U,则元函数返回BoolType<true>,否则返回BoolType<false>

如下我们借助编译器对重载函数的选择来完成模式匹配,以实现IsConvertible

// “tlp/traits/IsConvertible.h”

template<typename T, typename U>
struct IsConvertible
{
private:
    using  Yes = char;
    struct No { char dummy[2]; };

    static Yes test(U);
    static No  test(...);
    static T self();

public:
    using Result = BoolType<sizeof(test(self())) == sizeof(Yes)>;
};

#define __is_convertible(...)  typename IsConvertible<__VA_ARGS__>::Result

上面代码中,我们在IsConvertible中定义了静态函数test的两个重载版本,一个入参类型是U,另一个是随意类型入参(...出现在C++函数参数声明中表示不关心入参类型)。然后我们尝试把T传入test函数,如果T能够向U转型,则编译期会选择Yes test(U)版本,否则选择No test(...)版本。最后我们计算test返回类型的sizeof,就能判断出编译器选择了哪个版本(Yes和No是IsConvertible内部定义的两个类型,Yes的sizeof结果是1个字节,No是两个字节;sizeof是一个编译期运算符)。

在上面的实现中我们用了一个小技巧,我们并没有给test直接传入T的对象,因为这样做的话我们就要承受让T生成对象的开销,而且关键的是我们对T的构造函数一无所知。所以这里声明了一个返回类型为T的静态函数static T self(),然后把这个函数交给test。还记得我们前面说的“一切都是函数,一切都是类型”吗?我们用self函数替代类型T的对象传入test,在编译期就能获得结果,而且避免了创建对象的开销。

借助__is_convertible我们能够轻易的实现一个判断两个类型是否能够互相转型的元函数。

// “tlp/traits/IsConvertible.h”

#define __is_both_convertible(T, U)     __and(__is_convertible(T, U), __is_convertible(U, T))

上面代码中的__and()是我们前面介绍的对两个BoolType进行逻辑与运算的元函数。

现在我们可以这样使用:

__is_convertible(char, int)   // 返回__true()
__is_convertible(char, void*) // 返回__false()
__is_convertible(char*, void*)// 返回__true()

struct Base{};
struct Derived : Base {};

__is_convertible(Base*, Derived*) // 返回__false()
__is_convertible(Derived*, Base*) // 返回__true()

通过函数组合,我们还能实现出__is_base_of(),用来判断一个类型是否是另一个的父类。

// "tlp/traits/IsBaseOf.h"

#define __is_base_of(T, U)                      \
__and(__is_convertible(const U*, const T*)          \
      __and(__not(__is_eq(const T*, const void*)),          \
            __not(__is_eq(const T, const U))))

如上我们定义类型T是类型U的父类的意思就是:const U*可以向const T*转型,但是const T*不是const void*,同时const Tconst U不是相同类型。


递归

返回 C++11模板元编程 - 目录

相关文章

  • C++11 模板元编程 - 模式匹配

    C++模板元编程中,编译器对模板的特化版本选择相当于是在做模式匹配,这个我们已经比较熟悉了。下面我们借助这一特性实...

  • C++11 模板元编程 - 递归

    函数式语言依赖模式匹配和递归完成类似命令式语言里分支选择和循环迭代的功能。模板元编程中可以完成模式匹配的两种方式上...

  • C++11 模板元编程 - 元编程

    从本节开始我们将模板元编程当做一门独立的函数式语言来讨论它的方方面面。 所谓元编程,就是指可以产生程序的程序。由于...

  • C++11 模板元编程 - 模板元编程的应用

    本节开始我们通过使用C++模板元编程去解决一些实际问题,来展示模板元编程针对现实问题的使用方法和设计技巧。本节中的...

  • C++11 模板元编程 - 模板递归

    模板可以被递归调用,在模板递归的过程中,可以执行前面我们提到的两种编译期计算:数值计算和类型计算。 下面我们用模板...

  • C++11 模板元编程 - 元函数

    我们继续演进前面那个无聊的类型计算的例子,来得出元函数的定义。 前面我们实现了PointerOf,它对于传进的任意...

  • C++11 模板元编程 - 后记

    当1994年,Erwin Unruh在C++标准委员会上演示了通过C++模板在编译期计算素数的程序后,C++模板元...

  • C++11 模板元编程 - 前言

    熟悉C++的程序员都知道,C++是一门多范式编程语言,支持面向过程、面向对象、泛型编程以及函数式编程范式。然而提到...

  • C++11 模板元编程 - 目录

    前言 模板的基础知识模板的类型参数模板的默认参数模板的模板参数模板的特化模板的非类型参数模板的编译期计算数值计算类...

  • C++11 模板元编程 - 惰性

    C++对模板的具现化采用尽量惰性的原则。只有当你使用了模板的内部定义,编译器才会为模板生成对应的定义。 所以对于元...

网友评论

      本文标题:C++11 模板元编程 - 模式匹配

      本文链接:https://www.haomeiwen.com/subject/bewbettx.html