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/

相關文章
相關標籤/搜索