C 模板非型別形參的詳細講解

2022-09-21 14:12:08 字數 3003 閱讀 4027

前言

關於模板的非型別形參,網上有很多內容,c++primer只有大概一頁的闡述,但是都不夠清晰詳細。下面我盡可能從自己的角度去給大家描述一下非型別形參的相關細節。如果想進一步理解非型別形參以及模板內容可以閱讀c++template這本書,在4.1節,8.3.3節,13.2節都有相關解釋。

模板除了定義型別引數,我們還可以在模板定義非型別引數。

什麼是非型別形參?顧名思義,就是表示乙個固定型別的常量而不是乙個型別。

先舉乙個簡單的例子(模板類與模板函式都可以用非型別形參)

//例子1:

template class list;

char a = "saaa";;//全域性變數

char a2 = "saaa";;//區域性變數,寫在main函式裡面

char *b = "saaa";//全域性變數

char *const c = "saaa";//全域性變數,頂層指標,指標常量

pointert p1;//錯誤

pointert p2;//正確

pointert p22;//錯誤,區域性變數不能用作非型別引數

pointert p3;//錯誤,error c2975:「pointert」的模板引數無效,應為編譯時常量表示式

pointert p4;//錯誤,error c2970: 「c」: 涉及帶有內部鏈結的物件的表示式不能用作非型別引數

//關於指標常量和常量指標可以參考部落格

const用法總結(快速區分指標常量與常量指標)

這裡大家可能會有幾個疑問

①.到底為什麼字串不能作為實參?

答:我們看到上面p1的模板實參是"testvarchar",然而當我們在另乙個編譯單元(.cpp檔案)同樣宣告這麼乙個模板例項時,這兩個"testvarchar"的位址可能是不同的,編譯器傳遞給模板時就會傳遞傳遞不同的位址,從而導致這兩個模板例項是兩個不同且不相容的型別。這就是支援字串的問題所在。(這裡可能更深的涉及模板的實現原理)

②.變數b和c作為模板實參為什麼錯誤不同?

答:首先解釋b實參,b在這裡看做是乙個指標,是乙個全域性指標,但是他不是乙個常量表示式,所以b不對。我們再看看c,c相比於b對了乙個const修飾符,表示這個指標是乙個常量。然而const是乙個比較特別的關鍵字,他具有內部鏈結屬性(關於內連線參考部落格 理解c++的鏈結:c++內鏈結與外鏈結的意義),也就是說僅在定義這個變數的檔案內可見,不會造成不同編譯單元的混編時的鏈結錯誤。

這個特性對於模板來說可是有問題的,就像問題①所描述的,由於每個編譯單元可能都有乙個c變數,導致在編譯時,例項化多個c,而且c的位址還不同,這就造成二個模板的例項是兩個不同且不相容的型別。

③為什麼a變數作為實參可以?

答:我看過一些書籍,上面舉得例子都是用const修飾不行的情況下在加extern來形成extern constchara="saaa";這樣形式的語句,extern和const聯合使用確實可以壓制const的內部屬性。

這個a這裡可以看做乙個陣列型別,進一步理解陣列與指標的關係

附:char * itoa(int, char *, int); 第二個引數明明是char*,為什麼卻又不能是「char*」?

itoa這個函式大家應該多多少少接觸過,它的功能使把乙個整型按照你給的進製轉換成你想要的字串,也就是這個函式讓我覺得有必要再去研究一下字串陣列和字串指標的區別。

首先看itoa這個函式原型,char * itoa(int originnum, char * targetstr, int standard);

第乙個引數你的整型資料,第二個是乙個字串,第三個是乙個int型表示n進製。

現在我們測試一下,

char *str=「hello」;

int num=123;

_itoa_s(num, str,10); //vs c++下使用會提示編譯錯誤

itoa(num,str,10);//codeblocks下執行會崩潰,正常環境下都會崩潰的

char str2=「hello」;

itoa(num, str2,10);//執行正常

這樣我們發現明明函式原型的引數就是char*,為什麼我們寫的str卻不行呢?

char *和char到底有什麼區別?

這裡,我們先從其本質說起。說到底乙個是陣列乙個是指標,兩者其實除了都能儲存字串外區別確實大了。最重要的一點區別就是記憶體分配(關於c語言變數在記憶體的儲存位置,大家可以參考…….),對於基本型別的單個變數與陣列我們都會為其在棧上申請空間來存放資料,而指標只是指向一塊記憶體的索引,所以char*宣告的只是指向常量區」hello」的指標。

這時候我們再看一下itoa的功能,它是要把num轉換成字串存在str裡面,然而這時候我們的str根本沒有一塊可以用的記憶體,當然會崩潰。

反觀陣列str2,在宣告的時候就已經在棧上分配記憶體了,這時候當然可以儲存資料了。

因此,必須要為其分配記憶體

char *t;

t = (char*)malloc(9*sizeof(char));

接著上面的例子,我們需要理解

char *str=「hello」;

char str2=「hello」;

這是兩種不同的操作,str是宣告乙個指標指向常量區的」hello」。而str2是宣告乙個abmqustr2陣列用來存放乙個」hello」字串的拷貝。總之,如果str動態申請記憶體的話,那麼在堆裡str指向的位置就會有乙個」hello」,棧裡面有str2指向的」hello」,常量區還有乙個」hello」。雖然都是賦值,差距卻非常大。

下面乙個例子進一步證明了這一點(vs2012):

const char*s1= "sa1";

const char*s2= "sa1";

程式設計客棧

if(s1== "sa1")

{ cout程式設計客棧

我們知道字串比較不能用 == 直接比較,需要用strcmp,因為上面的s1,s2,onaabmqume,str2都是指標,比較其實只是比較指標的大小。我們看到,只有上面的 s1 == "sa1"結果是true。因為s1,s2指向的都是常量區的「sa1」字串。oname,str2分別指向堆和棧區。

總結

模板非型別形參的詳細闡述

關於模板的非型別形參,網上有很多內容,c primer只有大概一頁的闡述,但是都不夠清晰詳細。下面我盡可能從自己的角度去給大家描述一下非型別形參的相關細節。如果想進一步理解非型別形參以及模板內容可以閱讀c template這本書,在4.1節,8.3.3節,13.2節都有相關解釋。模板除了定義型別引數...

模板非型別形參

今天看c primer看到16.4.2節內容,非型別形參的模板實參 自己突發奇想非型別形參是否支援型別轉換,於是鍵入double型資料,編譯器提示模板形參錯誤 vs2010,error 表示式必須包含整數或列舉型別 可見不僅不支援型別轉換,更不能允許形參為整型與列舉型別之外的型別。不見黃河不死心,於...

模板非型別形參

今天看c primer看到16.4.2節內容,非型別形參的模板實參 自己突發奇想非型別形參是否支援型別轉換,於是鍵入double型資料,編譯器提示模板形參錯誤 vs2010,error 表示式必須包含整數或列舉型別 可見不僅不支援型別轉換,更不能允許形參為整型與列舉型別之外的型別。不見黃河不死心,於...