美文网首页
C++11 模板元编程 - 鸭子类型

C++11 模板元编程 - 鸭子类型

作者: MagicBowen | 来源:发表于2016-09-16 08:35 被阅读703次

    模板为C++提供了鸭子类型(Duck typing)的特性。所谓鸭子类型,指的是代码关注的不是对象的类型本身,而是它被如何使用的。例如,在使用鸭子类型的语言中,我们编写一个函数可以接受一个任意类型的对象,只要它有走、游泳和嘎嘎叫方法。至于客户给它传入的是一只真正的鸭子,或是也能走、游泳和嘎嘎叫的其它类型对象,都没有关系。但是如果传入的对象中没有这些需要被调用的方法,就将引发一个错误。

    " When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. - James Whitcomb Riley, 1849-1916"

    我们看下面这个例子:

    template<typename T>
    T max(const T& t1, const T& t2)
    {
        return (t1 > t2)? t1 : t2;
    }
    

    max,只约束入参类型T支持>比较运算,而不关心它的具体类型。例如我们可以为max传入intfloat或者是实现了了opertator >的任何对象。

    直到现在,C++中模板对入参的约束都是通过对入参的使用方式来隐式体现的。而有的语言却可以显示约束。例如对于如下Haskell代码:

    max' :: (Ord a) => a -> a -> a
    max' x y
        | x > y     = x
        | otherwise = y
    

    如上max的定义前面通过函数声明:max' :: (Ord a) => a -> a -> a约束了入参的类型a必须满足Ord类型类的约束。类型类用于规范一组类型应该满足的特征,例如Ord要求满足它的类型必须能够进行标准的比较操作,如<><=>=等。

    C++17标准有可能会引入concept特性用来支持上述haskell中对类型特征进行显示约束的能力。显示化类型约束可以让代码更容易被理解,让编译器可以更准确的报告错误或者对代码更好地做出优化,同时也可以让IDE对语言更好地支持。

    鸭子类型为程序的书写带来了很多便利性,基本上动态语言(Python、Ruby)以及拥有类型推导的静态语言(C++、Haskell、Scala)都有这个特性。区别在于动态语言一般是在运行期发现类型不满足约束,而静态语言通过强大的类型推导可以在编译期就发现错误。

    回到最后,我们思考下,如果把C++模板元编程当做一门独立的语言,它自身是否支持鸭子类型呢?

    答案很明确,虽然模板为“运行时C++”提供了鸭子类型的能力,但模板元编程自身却不支持鸭子类型。

    例如下面的元函数明确要求其入参的型别为类型,所以你可以这样使用SizeOf<int>。但是一旦你传入一个数值SizeOf<5>,它就会报错。

    template<typename T>
    struct SizeOf
    {
        using Result = __int(sizeof(T));
    };
    

    我们之前总结过,模板可以操作的计算对象大体可以分为数值和类型两大类,一旦我们把模板当做编译期函数来看,就会发现它是强类型的。一个模板声明其入参是数值型,就不能接收类型作为入参,反之亦然!这就是为何我们为了提高元函数的组合复用能力,将所有的数值也封装成了类型。我们统一模板元编程的计算对象类型,就相当于把一切都变成了鸭子,间接地也得到了鸭子类型的好处。

    最后,我们单独看待模板元编程的时候,它相当是一门解释型语言!C++编译器直接面对模板元编程的源代码进行编译期计算,这时我们可以将C++编译器看做是模板元编程的解释器,它一边解释一边执行,解释结束之时也是程序执行完毕之时。有趣吧?在这个角度看模板元编程反而更像是一门脚本语言。


    总结:两阶段的C++语言

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

    相关文章

      网友评论

          本文标题:C++11 模板元编程 - 鸭子类型

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