C++Template 模版的本质

    C++ Template  模版的本质html

     自动化是人类进化的动力程序员

  AlexCoolweb

                             本文出现的目的,就是尽可能让人们理解C++模版设计的思想, 属于模板的心法。算法

 

我想知道上帝是如何创造这个世界的。我对这个或那个现象,这个或那个元素的能谱不感兴趣。我要知道的是他的思想。其余都是细节。express

——爱因斯坦编程

 

模版最初的目的就是为了减小重复代码,因此模版出现的目的就是为了解放C++程序员生产力。设计模式

 content

 C++模版是什么

     什么是参数化容器类

     什么是通用算法

模板是怎么解决上面问题的

      C++实现参数化类(class template)技术

      C++实现模板函数(function template)技术

实现C++模板的几个核心技术

模版典型的应用场景有哪些

对模版的展望

更多细节请参考资料和进一步阅读

 

 C++模版是什么?安全

程序 = 数据结构  + 算法数据结构

                                              ---Niklaus EmilWirth框架

最初C++是没有标准库的,任何一门语言的发展都须要标准库的支持,为了让C++更强大,Bjarne Stroustrup以为须要给C++提供一个标准库,但标准库设计须要一套统一机制来定义各类通用的容器(数据结构)和算法,而且能很好在一块儿配合,这就须要它们既要相对的独立,又要操做接口保持统一,并且可以很容易被别人使用(用到实际类中),同时又要保证开销尽可能小(性能要好)。 Bjarne Stroustrup 提议C++须要一种机制来解决这个问题,因此就催生了模板的产生,最后经标准委员会各路专家讨论和发展,就发展成现在的模版, C++ 第一个正式的标准也加入了模板。

C++模版是一种解决方案,初心是提供参数化容器类和通用的算法(函数)。

什么是参数化容器类?

首先C++是能够提供OOP(面向对象)范式编程的语言,因此支持类概念,类自己就是现实中一类事物的抽象,包括状态和对应的操做,打个比喻,大多数状况下咱们谈论汽车,并非指具体某辆汽车,而是某一类汽车(某个品牌),或者某一类车型的汽车。

因此咱们设计汽车这个类的时候,各个汽车品牌的汽车大致框架(骨架)都差很少,都是4个轮子一个方向盘,并且操做基本上都是相同的,不然学车都要根据不一样厂商汽车进行学习,因此咱们能够用一个类来描述汽车的行为:

class Car

{

public:

  Car(...);

  //other operations

  ...

private:

Tire m_tire[4];

Wheel m_wheel;

//other attributes

 ...

};

但这样设计扩展性不是很好,由于不一样的品牌的车,可能方向盘形状不同,轮胎外观不同等等。因此要描述这些不一样咱们可能就会根据不一样品牌去设计不一样的类,这样类就会变得不少,就会产生下面的问题:

1 代码冗余,会产生视觉复杂性,自己类似的东西比较多;

2 用户很难经过配置去实现一辆车设计,很差定制化一个汽车;

3 若是有其中一个属性有新的变化,就得实现一个新类,扩展代价太大。

这个时候,就但愿这个类是能够参数化的(属性参数化),能够根据不一样类型的参数进行属性配置,继而生成不一样的类。

类模板就应运而生了,类模板就是用来实现参数化的容器类。

 

什么是通用算法?

程序=数据结构+算法;

算法就是对容器的操做,对数据结构的操做,通常算法设计原则要知足KISS原则,功能尽可能单一,尽可能通用,才能更好和不一样容器配合,有些算法属于控制类算法(好比遍历),还须要和其余算法进行配合,因此须要解决函数参数通用性问题。举个例子:

之前咱们实现通用的排序函数多是这样:

void sort (void* first, void* last, Cmp cmp);

这样的设计有下面一些问题:

1.为了支持多种类型,须要采用void*参数,可是void*参数是一种类型不安全参数,在运行时候须要经过类型转换来访问数据。

2.由于编译器不知道数据类型,那些对void*指针进行偏移操做(算术操做)会很是危险(GNU支持),因此操做会特别当心,这个给实现增长了复杂度。

因此要知足通用(支持各类容器),设计复杂度低,效率高,类型安全的算法,模板函数就应运而生了,模板函数就是用来实现通用算法并知足上面要求。

 

模板是怎么解决上面问题的?

C++标准委员会采用一套相似函数式语言的语法来设计C++模板,并且设计成图灵完备 (Turing-complete)(详见参考),咱们能够把C++模板当作是一种新的语言,并且能够当作是函数式编程语言,只是设计依附在(借助于)C++其余基础语法上(类和函数)。

 

 

 C++实现参数化类(class template)技术:

1.定义模板类,让每一个模板类拥有模板签名。

模板类语法:

template<typename T>

class X{...};

 

  • 上面的模板签名能够理解成:X<typename T>; 主要包括模板参数<typename T>和模板名字X(类名), 基本的语法能够参考《C++ Templates: The Complete Guide》,《C++ primer》等书籍。
  • 模板参数在形式上主要包括四类:  

     为何会存在这些分类,主要是知足不一样类对参数化的需求:

  • type template parameter,类型模板参数,以class或typename 标记;
    • 此类主要是解决朴实的参数化类的问题(上面描述的问题),也是模板设计的初衷。
  • non-type template parameter,非类型模板参数,好比整型,布尔,枚举,指针,引用等;
    • 此类主要是提供给大小,长度等整型标量参数的控制,其次还提供参数算术运算能力,这些能力结合模板特化为模板提供了初始化值,条件判断,递归循环等能力,这些能力促使模板拥有图灵完备的计算能力。
  • template template parameter,模板参数是模板;
    • 此类参数须要依赖其余模板参数(做为本身的入参),而后生成新的模板参数,能够用于策略类的设计policy-base class。
  •  parameter pack,C++11的变长模板参数 ;
    • 此类参数是C++11新增的,主要的目的是支持模板参数个数的动态变化,相似函数的变参,但有本身独有语法用于定义和解析(unpack),模板变参主要用于支持参数个数变化的类和函数,好比std::bind,能够绑定不一样函数和对应参数,惰性执行,模板变参结合std::tuple就能够实现。

2.在用模板类声明变量的地方,把模板实参(Arguments)(类型)带入模板类,而后按照匹配规则进行匹配,选择最佳匹配模板.

  • 模板实参和形参相似于函数的形参和实参,模板实参只能是在编译时期肯定的类型或者常量,C++17支持模板类实参推导。

3.选好模板类以后,编译器会进行模板类实例化--记带入实际参数的类型或者常量自动生成代码,而后再进行一般的编译。

 

C++实现模板函数(function template)技术:

模板函数实现技术和模板类形式上差很少:

template<typename T>
retType  function_name(T  t);

其中几个关键点:

  1. 函数模板的签名包括模板参数,返回值,函数名,函数参数,  cv-qualifier;
  2. 函数模板编译顺序大体:名称查找(可能涉及参数依赖查找)->实参推导->模板实参替换(实例化,可能涉及 SFINAE)->函数重载决议->编译;
  3. 函数模板能够在实例化时候进行参数推导,必须知道每一个模板的实参,但没必要指定每一个模板的实参。编译器会从函数实参推导缺失的模板实参。这发生在尝试调用函数、取函数模板地址时,和某些其余语境中;
  4. 函数模板在进行实例化后(template argument deduction/substitution)会进行函数重载解析(overloading resolution, 此时的函数签名不包括返回值;
  5. 函数模板实例化过程当中,参数推导不匹配全部的模板或者同时存在多个模板实例知足,或者函数重载决议有歧义等,实例化失败;
  6. 为了编译函数模板调用,编译器必须在非模板重载、模板重载和模板重载的特化间决定一个无歧义最佳的模板 ;

实现C++模板的几个核心技术:

  1. SFINAE -Substitution failure is not an error ;

      要理解这句话的关键点是failure和error在模板实例化中意义,模板实例化时候,编译器会用模板实参或者经过模板实参推导出参数类型带入可能的模板集(模板备选集合)中一个一个匹配,找到最优匹配的模板定义,

        Failure:在模板集中,单个匹配失败;

        Error: 在模板集中,全部的匹配失败;

因此单个匹配失败,不能报错误,只有全部的匹配都失败了才报错误。

        2.  模板特化

      模板特化为了支持模板类或者模板函数在特定的状况(指明模板的部分参数(偏特化)或者所有参数(彻底特化))下特殊实现和优化,而这个机制给与模板某些高阶功能提供了基础,好比模板的递归(提供递归终止条件实现),模板条件判断(提供true或者false 条件实现)等。

       3. 模板实参推导

模板实参推导机制给与编译器能够经过实参去反推模板的形参,而后对模板进行实例化,具体推导规则见参考;

       4. 模板计算

模板参数支持两大类计算:

    • 一类是类型计算(经过不一样的模板参数返回不一样的类型),此类计算为构建类型系统提供了基础,也是泛型编程的基础;
    • 一类是整型参数的算术运算, 此类计算提供了模板在实例化时候动态匹配模板的能力;实参经过计算后的结果做为新的实参去匹配特定模板(模板特化)。

       5. 模板递归

 模板递归是模板元编程的基础,也是C++11变参模板的基础。

 

模版典型的应用场景有哪些?

  1. C++ Library:

 能够实现通用的容器(Containers)和算法(Algorithms),好比STL,Boost等,使用模板技术实现的迭代器(Iterators)和仿函数(Functors)能够很好让容器和算法能够自由搭配和更好的配合;

        2  C++ type traits

经过模板技术,C++  type traits实现了一套操做类型特性的系统,C++是静态类型语言,因此须要再编译时候对类型进行检查,这个时候type traits能够提供更多类型信息给编译器,能让程序能够作出更多选择和优化。 C++创始人对traits的理解:

”Think of a trait as a small object whose main purpose is to carry information used by another object or algorithm to determine "policy" or "implementation details". - Bjarne Stroustrup

        3. Template metaprogramming-TMP

随着模板技术的发展,模板元编程逐渐被人们发掘出来,metaprogramming本意是进行源代码生成的编程(代码生成器),同时也是对编程自己的一种更高级的抽象,比如咱们元认知这些概念,就是对学习自己更高级的抽象。 TMP经过模板实现一套“新的语言”(条件,递归,初始化,变量等),因为模板是图灵完备,理论上能够实现任何可计算编程,把原本在运行期实现部分功能能够移到编译期实现,节省运行时开销,好比进行循环展开,量纲分析等。

                                                           (取自参考文献6)

        4. Policy-Based Class Design

C++ Policy class design 首见于 Andrei Alexandrescu 出版的 《Modern C++ Design》 一书以及他在C/C++ Users Journal杂志专栏 Generic<Programming>,参考wiki。经过把不一样策略设计成独立的类,而后经过模板参数对主类进行配置,一般policy-base class design采用继承方式去实现,这要求每一个策略在设计的时候要相互独立正交。STL还结合CRTP (Curiously recurring template pattern)等模板技术,实现相似动态多态(虚函数)的静态多态,减小运行开销。

         5. Generic Programming

因为模板这种对类型强有力的抽象能力,能让容器和算法更加通用,这一系列的编程手法,慢慢引伸出一种新的编程范式:泛型编程。 泛型编程是对类型的抽象接口进行编程,STL库就是泛型编程经典范例。     

 

对模版的展望

  1. 模版带来的缺点是什么?

     模板的代码和一般的代码比起来,可读性不好,因此很难维护,对人员要求很是高,开发和调试比较麻烦。 对模板代码,实际上很难覆盖全部的测试,因此为了保证代码的健壮性,须要大量高质量的测试,各个平台(编译器)支持力度不同,可移植性不能彻底保证。模板颇有可能会隐式地增长二进制文件的大小,多个实例等,因此模板在某些状况下有必定代价;

        2. 基于模板的设计模式

      随着C++模板技术的发展,以及大量的经验总结,逐渐造成了一些基于模板的经典设计,好比STL里面的特性(traits),策略(policy),标签(tag)等技法;Andrei Alexandrescu 提出的Policy-Based Class Design;以及Jim Coplien的curiously recurring template pattern (CRTP),以及衍生Mixin技法;或许将来,基于模板能够衍生更多的设计模式。

        3.  模板的将来

C++标准准备引进Concepts(Concepts: The Future of Generic Programming by Bjarne Stroustrup)解决模板调试问题,对模板接口进行约束;随着模板衍生出来的泛型编程,模板元编程,模板函数式编程等理念的发展,未来也许会发展出更抽象,更通用编程理念。模板自己是图灵完备的,因此能够结合C++其余特性:编译期常量和常量表达式,编译期计算,继承,友元friend等开阔出更多优雅的设计,好比元容器,类型擦除,自省和反射等,未来会出现更多设计。

 

但愿本文对想学习C++模板的同窗有必定的帮助,有不对的地方还请指正,多谢!

更多细节请参考资料和进一步阅读:

        1《The design and Evolution of C++ 》Bjarne Stroustrup;

        2. C++ Templates are Turing Complete,Todd L. Veldhuizen,2003(做者网站已经停了,archive.org 保存的版本,archive.org 可能被限制浏览);

3.  模板细节:

      wikipedia.org, cppreference.com(C++,模板template, Template metaprogramming, CRTP (Curiously recurring template pattern), Substitution failure is not an    error (SFINAE), template_argument_deduction ,Policy-based_class design, Expression templates,等);

            C++ Programming/Templates/Template Meta-Programming

       4.模板的基本语法:

         C++标准ISO+IEC+14882-1998,2003,2011;

       《C++ Templates: The Complete Guide》 by David Vandevoorde, Nicolai M. Josuttis;

       5.模板设计进价:

   Andrei Alexandrescu 的 《Modern C++ Design》;

     候捷的《STL源码剖析》;

        More C++ Idioms:wikipedia.org

       6. 模板元编程:

Abrahams, David; Gurtovoy, Aleksey. C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond. Addison-Wesley. ISBN 0-321-22725-5.

Metaprogramming in C++,Johannes Koskinen,2004

Boost 的MPL库

7.Blog and papers:

Coplien, James O. (February 1995). "Curiously Recurring Template Patterns" (PDF). C++ Report: 24–27.

https://en.wikipedia.org/wiki/Mixin

http://www.cnblogs.com/liangliangh/p/4219879.html

http://b.atch.se/posts/non-constant-constant-expressions/

相关文章
相关标签/搜索