歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++ 模板元編程學習心得體會

C++ 模板元編程學習心得體會

日期:2017/3/1 9:31:23   编辑:Linux編程

快速翻了一遍傳說中的、大名鼎鼎的 modern c++ design,钛合金狗眼頓時不保,已深深被其中各種模板奇技淫巧傷了身。。。論語言方面的深度,我看過的 c++ 書裡大概只有 insight c++ object model 能與之一戰吧?難怪 Herb 老喜歡調侃 Andrei 在模板方面是個可怕的家伙,就從這本書的質量來看,Andrei 當之無愧。

c++ 模板元編程的力量遠比第一眼印象裡所能想像得要強大,當然,這個結論並不明顯,很多時候人們也就拿模板當作減少重復代碼的工具簡單使用,很少有人會像寫 STL, boost, loki 那樣子正兒八經完全以模板為根基進行創作,個中原因是多樣的,一個結果就是,模板的威力即使在很多 c++ 熟手的手下也不容易充分展現出來,當然從工程的角度來講,這並非就是壞事,並不見得非得窮盡語言的高級特性,寫出讓人驚歎的代碼才是好代碼,實戰中更多時候講究的是簡潔,易讀,好上手好維護這些基本原則,特別是當團隊中人員在語言水平上參差不齊時更是如此,杜甫的詩也許更工整嚴謹,但白居易的詩則顯然更老少皆宜,這個體會我是在閱讀 boost.proto 的代碼時得來的,好奇的讀者可以自行去了解一下,注意做好安全措施。

從語言特性上來說,模板元編程具備了一個完備的編程語言所必需的一些基本結構,比如說,循環,分支,判斷等,當然這些結構在模板中可能也不太明顯,例如循環,它的實現關鍵在於使用遞歸,出口點在特化,typedef 和 enum 則可以當成是編譯時的變量,它們都能在編譯時遞歸式地依賴於別的模板,尤其是 enum, 甚至可以通過使用三元操作符(:?)實現編譯時判斷,而至於分支(if/else),它們的實現顯然就在於特化了。

以上的說辭可能有些虛無,下面以 Loki 中光彩奪目的 typelist 為例簡單展示一下模板都能做些什麼。

typelist 是這樣一個東西,你可以把它看成是一個鏈表,該鏈表中放的是類型,具體結構定義如下:

template <class T, class U>
struct Typelist
{
  typedef T Head;
  typedef U Tail;
};

Head 是 typelist 中當前節點所保存的 type, Tail 是 typelist 中該節點之後的其它部分,可以看成是鏈表中的 next 指針,怎麼判斷哪個節點是 typelist 的最後一個節點呢?在鏈表中,next 為空的節點是最後一個節點,同理,在 typelist 中我們可以定義一個空的結點:

struct NullType
{
};

有了上如上的定義,於是我們可以如下這樣子來使用 typelist 了:

typedef Typelist<char, Typelist<int, Typelist<short, NullType> >

但是上面的寫法怎麼看都很難用而且極端不美觀,在這個看臉的時代這怎麼行,所以 Loki 定義了一大堆宏來減輕使用者的負擔,看這裡,因為當時 c++ 的標准還不支持 variadic template parameter,這些宏事實上是相當死板惡心的,但這也是沒法的事,不看它的實現就好了。

就這麼一個簡單的結構,現在我們要讓它支持查找,定位,插入,刪除等常規操作,是不是覺得有些為難甚至覺得不可能? 答案是這些操作都是可行的,比如說查找,現在給定一個如上定義的 typelist,怎麼判斷該 typelist 中是否存在某一個特定類型呢?

答案如下,其它的操作本質上差不多,有興趣的讀者可自行挑戰一下,就不在這裡啰嗦:

template<class TL, class T> struct IndexOf;

// 當搜索空的 typelist 時,結果為 -1.

template<>
struct IndexOf<NullType, T>
{
  enum { value = -1 };
};

// 當前節點的類型為所想要搜索的類型時

template<class T, class Tail>
struct IndexOf<Typelist<T, Tail>, T>
{
  enum { value = 0 };
};

// 當前節點不是所查找的類型時,遞歸地在 Tail 中進行查找。

template<class Head, class Tail, class T>
struct IndexOf<Typelist<Head, Tail>, T>
{
  enum { in_tail = IndexOf<Tail, T>::value };
  
  // 使用三元操作符進行判斷,T 是否在 Tail 中存在。
  enum { value = (in_tail >= 0)? 1 + in_tail: -1 };
};

從上面的例子我們可以看到,因為 c++ 支持對模板遞歸式的解析(也就是一個模板依賴於另一個模板時,先解釋被依賴的模板),使得模板事實上有了很強的編譯時運行的能力,這種能力表面上看起來可能不容易操控,但卻顯然是潛力無限的,不過它的缺點也比較明顯:

  1. 編譯時代碼與運行時代碼攪在一起,在處理復雜問題時,程序的邏輯可能不容易讀懂。
  2. 編譯時調試現階段的支持還不夠好。

網絡上關於 c++ 模板元編程的討論有很多,模板的各種能力技巧也漸漸被越來越多的人所發現所挖掘,但是在實際的工作中,對很多人來說模板元編程卻仍一直處於比較保守的狀態,到底過分依賴模板元編程缺點還是太明顯,就我的見聞來說,完全基於模板元編程做出來的比較出名的工具型的東西,主要有兩個:boost spirit 與 boost proto. 它們的使用體驗,老實說都不是很好。。。特別是 spirit。而至於它們的實現,對有興趣練習深入這方面技能的程序猿來說,這兩者倒確實是不可多得好素材,尤其是 proto, 代表了一個高峰。

好消息是,伴隨著 c++11 的到來,好些眾人期盼以久的新特性終於從理想照進現實,尤其是 variadic parameter 的加入,可以預見將再度大大提升模板的能力,c++ 標准沉寂近10年後迎來了一個新時期,甚至還有人曾經提議要加入 static_if,concept 等概念也在醞釀中了,變化是唯一永恆不變的東西,你,作好准備了嗎?

------------------------------分割線------------------------------

C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm

讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm

讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm

讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm

將C語言梳理一下,分布在以下10個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  10. Linux-C成長之路(十):其他高級議題

Copyright © Linux教程網 All Rights Reserved