天天看點

C++ Traits程式設計技法--從疊代器的設計看參數推導與類型推導 疊代器與相應類型推導 解決方法一函數模版的參數推導機制 解決方法二使用嵌套依賴類型(nested depended name) 特性萃取機器–偏序化實作支援原生類型 STL中疊代器的設計

在寫作泛型函數或代碼時,我們可能存在這樣的需要:與參數相關的其它類型,比如一個疊代器的值的類型,在算法中運用疊代器時,很可能會用到其也叫相應類型(associate type)。

什麼是相應類型?

疊代器所指之物的類型就是其中一個。如果我們的算法中有必要聲明一個變量,以”疊代器所指對象的類别”為型号。

本文要向大家展示一個函數模闆推導機制使用技法,這個在stl的疊代器和許多排序算法中廣泛使用。

考慮一個情況,我們在寫一個泛型函數,它接受一對疊代器,要做的事就是對這一對疊代器之間的元素進行排序,其中将出現這幕:我需要對兩個值進行交換。不知道大家有沒有寫過這樣的代碼,現在的問題是如何實作這兩個值的交換?

如:

1

2

3

4

5

很顯然我們需要了解到疊代器所指向的具體類型,這該怎麼好呢

畢竟如果c++沒有隻支援sizeof(),并不支援typeof()

有些人說了我們有rtt1機制中的typeid(),是的很好,但是那擷取到的也隻是類型名稱name,而不是類型type,更不可能來做變量聲明

解決的方法:利用函數模版(function template)的參數推導(argument deducation)機制。

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

參數推導機制,以func()為對外接口,卻把實際的操作全部置于func_impl()之中,由于func_impl()是一個函數模版(function template),一旦被調用,編譯器會自動進行template的參數推導,于是導出類型t的問題,順利得到了解決。

但是疊代器相應類型不隻是“疊代器所指對象的類型”一種而已

根據經驗,最常用的相應類型有5種,然而并非任何情況下任何一種都可利用函數模版的參數推導機制來取得。

是以我們需要更一般的解法。

也稱為traits程式設計技法,是stl中廣泛使用的技巧

疊代器所指對象的類型,稱為疊代器的value type,上述的參數類型推導技巧雖然可用于value type,卻并不對所有的相應類型可用。

前面的情況,通過隐藏了中間層,通過函數模版自己去推導參數類型,我們的接口也隻是關心疊代器而不在意其指向的,但是如果我們的對外接口不能不了解到其相應類型的時候,這種方法就不适用了。

比如如下情況,萬一value type必須用于函數的傳回值,就束手無策了。

畢竟函數的“template參數推導機制”推而導之的隻是參數,無法推導函數的回返值類型。

我們需要其他方法,有什麼好的方法呢,還記得之前提到的typename的以及嵌套依賴類型(nested depended name)麼。

那不就可以麼,我們不妨一試

29

30

31

32

33

34

35

36

37

38

func()的傳回值被定義為typename iter::value_type

内嵌類型被定義為 typedef t value_type

由于t是一個template參數,在它被編譯器具體化之前,編譯器對t一無所知,換句話說,編譯器此時并不知道<code>myiter&lt;t&gt;::value_type</code>代表的是一個類型還是一個成員函數或者說是一個資料成員,

而這個typename關鍵字的用意就在于告訴編譯器這個是一個類型,如敝才能順利通過編譯。

這樣我們的問題似乎得到了解決,但是這裡有一個隐晦的陷阱:并不是所有的疊代器都是類class type,比如有原生的指針就不是。如果不是class type,就無法為它定義内嵌類型。

但是這顯然不是我們做模版類所期待的,在stl(甚至于整個泛型思維)絕對接受原生的指針作為一種疊代器,所有我們上面的方法還不夠,有沒有什麼方法可以讓上述的一般化概念針對特定情況(比如原生的指針類型)做特殊化處理呢?

那麼這就是template partial specialization可以完成的

如果class template擁有一個以上的template參數,那麼可以針對其中某個(或者多個,但是不能全部)的參數進行特化工作。

換句話說,我們可以在泛化設計中提供一個特化的版本。即通過将泛化版本中的某些template參數賦予明确的指定。

關于函數模版的特化問題,不是我們今天的重點,要了解詳細資訊,請參見 <a href="http://blog.csdn.net/gatieme/article/details/50953564">c++模闆的特化詳解(函數模版特殊,類模版特化)</a>

通過針對任何template參數的模版,進行特化,得到一個限制了參數對象的特化版本。

有了這個利器,我們就可以解決前述的”内嵌類型”未能解決的問題,先前的問題是,原生的指針并非class,是以無法為他們定義内嵌型别。

現在我們可以通過對疊代器指針為參數的特化對象,來設計可以接收原生指針的特殊疊代器。

是以我們設計出一個中間層,并添加一個value type内嵌對象

這個所謂的traits疊代器說明,如果iter定義有自己的value type,那麼就通過這個traits的作用,萃取出來的value_type成員就是iter::value_type。

換句話說,如果i定義有自己的value type,那麼先前的那個func就成為

增加iterator_traits這一中間層,現在我們為iterator_traits添加特化版本。

通過這種方式,原生的指針比如int *雖然不是類對象class type,亦可通過traits取其value type。

于是我們的疊代器支援原生的指針類型了。

但是這樣子夠了麼,請看下面的例子,

當我們使用指向const常數對象的指針時,看看發生了什麼

獲得到的是一個const int,而不是int。很顯然這個并不是我們所期望?

比如我們利用這種方式來聲明一個臨時變量,使其類别與疊代器的value type相同,而現在,聲明一個無法指派(因const之故)的暫時變量,并沒有什麼卵用。

這一切的一切,就隻是因為我們擷取到的疊代器的value type是一個pointer-to-const

現在ok了,不管是針對疊代器myiterm,還是原生的指針int ,甚至是const int ,都可以通過traits取出正确的value type

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

最後通過traits這個特性萃取機角色,可以巧妙的萃取出各個疊代器的屬性。

這點我們可以在stl源碼的找到對應的設計,參見 <code>stl_iterator_base.h</code>

由于

轉載:http://blog.csdn.net/gatieme/article/details/50950726

繼續閱讀