天天看點

C++ Primer 學習筆記_76_模闆與泛型程式設計 --模闆定義[續]模闆與泛型程式設計

四、模闆類型形參

類型形參由關鍵字class或 typename後接說明符構成。在模闆形參表中,這兩個關鍵字具有相同的含義,都指出後面所接的名字表示一個類型。

模闆類型形參可作為類型說明符在模闆中的任何地方,與内置類型說明符或類類型說明符的使用方式完全相同。具體而言,它可以用于指定傳回類型或函數形參類型,以及在函數體中用于變量聲明或強制類型轉換。

1、typename與class的差別

在函數模闆形參表中,關鍵字typename和 class具有相同含義,可以互換使用,兩個關鍵字都可以在同一模闆形參表中使用:

使用關鍵字typename代替關鍵字class指定模闆類型形參也許更為直覺,畢竟,可以使用内置類型(非類類型)作為實際的類型形參,而且,typename更清楚地指明後面的名字是一個類型名。但是,關鍵字typename是作為标準C++的組成部分加入到C++中的,是以舊的程式更有可能隻用關鍵字class。

2、在模闆定義内部指定類型

除了定義資料成員或成員函數外,類還可以定義類型成員。例如,标準庫的容器類定義了不同的類型,如size_type,使我們能夠以獨立于機器的方式使用容器。如果要在函數模闆内部使用這樣的類型,必須告訴編譯器我們正在使用的名字指的是一個類型。必須顯式地這樣做,因為編譯器(以及程式的讀者)不能通過檢查得知,由類型形參定義的名字何時是一個類型何時是一個值。例如,考慮下面的函數:

預設情況下,編譯器假定這樣的名字指定資料成員,而不是類型。

如果希望編譯器将size_type當作類型,則必須顯式告訴編譯器這樣做:

通過在成員名前面加上typename作為字首,可以告訴編譯器将成員當做類型。通過編寫typenameparm::size_type,指出綁定到Parm的類型的size_type成員是類型的名字。當然,這一聲明給用執行個體化fcn的類型增加了一個職責:那些類型必須具有名為size_type的成員,而且該成員是一個類型。

【最佳實踐】

如果拿不準是否需要以typename指明一個名字是一個類型,那麼指定它是個好主意。在類型之前指定typename沒有害處,是以,即使typename是不必要的,也沒有關系。

五、非類型模闆形參

模闆形參不必都是類型。

在調用函數時非類型形參将用值代替,值的類型在模闆形參表中指定。

模闆非類型形參是模闆定義内部的常量值,在需要常量表達式的時候,可使用非類型形參指定數組的長度。

當調用array_init時,編譯器從數組實參計算非類型形參的值:

編譯器将為array_init調用中用到的每種數組執行個體化一個array_init版本。

類型等價性與非類型形參

對模闆的非類型形參而言,求值結果相同的表達式将認為是等價的。比如下面的兩個array_init調用引用的是相同的執行個體– array_init<int,42>:

六、編寫泛型程式

編寫模闆時,代碼不可能針對特定類型,但模闆代碼總是要對将使用的類型做一些假設。例如,雖然compare函數從技術上說任意類型都是有效的,但實際上,執行個體化的版本可能是非法的。

産生的程式是否合法,取決于函數中使用的操作以及所用類型支援的操作:

如果用不支援<操作符的對象調用compare,則調用是無效的:

【小心地雷】

在函數模闆内部完成的操作限制了可用于執行個體化該函數的類型。程式員的責任是:保證用作函數實參的類型實際上支援所用的任意操作,以及保證在模闆使用那些操作的環境中那些操作運作正常!

編寫獨立于類型的代碼

編寫模闆代碼時,對實參類型的要求盡可能少是很有益的。

雖然簡單,但它說明了編寫泛型代碼的兩個重要原則:

1)模闆的形參是const引用

2)函數體中的測試隻用<比較

通過将形參設為const引用,就可以允許使用不允許複制的類型。而且,如果有比較大的對象調用compare,則這個設計還可以使函數運作得更快。

比較下面兩段程式:

與:

下面的程式可以減少對可用于compare函數的類型的要求,這些類型必須支援<,但不必支援>

【警告:連結時的編譯時錯誤】

一般而言,編譯模闆時,編譯器可能會在三個階段中辨別錯誤:

第一階段是編譯模闆定義本身時。在這個階段中編譯器一般不能發現許多錯誤,可以檢測到諸如漏掉分号或變量名拼寫錯誤一類的文法錯誤。

第二個錯誤檢測時間是在編譯器見到模闆的使用時。在這個階段,編譯器仍沒有很多檢查可做。對于函數模闆的調用,許多編譯器隻檢查實參的數目和類型是否恰當,編譯器可以檢測到實參太多或太少,也可以檢測到假定類型相同的兩個實參是否真地類型相同。對于類模闆,編譯器可以檢測提供的模闆實參的正确數目。

産生錯誤的第三個時間是在執行個體化的時候,隻有在這個時候可以發現類型相關的錯誤。根據編譯器管理執行個體化的方式,有可能在連結時報告這些錯誤。

重要的是,要認識到編譯模闆定義的時候,對程式是否有效所知不多。類似地,甚至可能會在已經成功編譯了使用模闆的每個檔案之後出現編譯錯誤。隻在執行個體化期間檢測錯誤的情況很少,錯誤檢測可能發生在連結時。

繼續閱讀