C 變長模板引數

2022-03-11 15:13:24 字數 2891 閱讀 4408

**自:

c++11 語言核心的改進中,最為關注的有 rvalue reference (這裡有一篇拙作),lambda,variadic template。

rvalue 規則稍微複雜,但一旦理解和記住了,應用上就沒有什麼困難。lambda 其實是乙個「很自然」的語言設施,除了語法稍顯詭異之外,習慣了就能馬上用上,而且是能廣泛用上的好東西。variadic template 這個新特性不像前兩者,它本身的語法規則並不複雜,但是應用的時候確比較費腦子,好在這個技術主要是用在庫的實現中,乙個實現得好的庫,並不會讓我們對實現的細節作任何的要求

。variadic template 特性本身是乙個很自然的需求,它完善了 c++ 的模板設計手段。原來的模板引數可以使類和函式的引數型別「任意化」,如果再加上「引數個數的任意化

」,那麼在引數方面的設計手段就基本上齊備了,有了variadic template 顯然可以讓設計出來的函式或是類有更大的復用性。因為有很多處理都是與「處理物件的個數」關係不大的,比如說打屏(printf),比如說比較大小(max,min),比如函式繫結子(bind,function要對應各種可能的函式就要能「任意」引數個數和型別)。如果不能對應任意個引數,那麼就總會有人無法重用已有的實現,而不得不再重複地寫乙個自己需要的處理,而共通庫的實現者為了盡可能地讓自己寫的類(函式)能復用在更多的場景,也不得不重複地寫很多的**或是用詭異的技巧,巨集之類的去實現有限個「任意引數」的對應。(像tr1中的 bind 等)。

宣告乙個帶有可變引數個數的模板的語法如下所示:

template classtuple;

tuple a;  // use it like this

在模板引數 element 左邊出現省略號 ... ,就是表示 element 是乙個模板引數包(template type parameter pack)。parameter pack(引數包)是新引入 c++ 中的概念,比如在這個例子中,element 表示是一連串任意的引數打成的乙個包。比如第2行中,element 就是 int, string這個引數的合集。不僅「型別」的模板引數(也就是typename定義的引數)可以這樣做,非型別的模板引數也可以這樣做。比如下面這個例子:

template

classarray;

array rotation_matrix; //3x3 ratiation matrix

現在我們知道parameter pack了,怎麼在程式中真正具體地去處理打包進來的「任意個數」的引數呢?我原來以為,編譯器會提供一些像get_param<1>(element) 之類的內建的「引數抽取函式」給程式設計師使用結果不是!!看來我的思路還是太「過程式了」。其實 c++11 用的是 unpack 和類似函式過載似的「模板特化」來抽取引數的。這是應用 variadic tempate 最「坑爹」的部分,因為它要求對「遞迴」和「人肉**展開」有一定的功力啊。還是看例子吧:

template classtuple;

template

classtuple: privatetuple;

template<>

classtuple<> ;

第1行宣告了乙個可以對應任意引數的tuple類,第2行到7行宣告了這個類的乙個部分特化,注意,這就是抽取引數的典型方法了。給個圖還是最方便用來理解的:

(如果看不全,點選它可以看全圖)

只說明一下針對 parameter pack 相對的另乙個概念,模板引數後面帶省略號 ... 就是乙個解包(unpack),會把這個引數所表示的引數列表解開後去匹配新的模板,或是進行模板展開。其它的,

我覺得有上面這個圖,實在不需要再多費什麼筆墨去講這一部分了。

新的標準庫里,有很多個庫都直接依賴於 variadic template 這個語言特性,比如,tuple,bind,function。但他們比較複雜,也不夠「驚豔」。c++ 老爸的 c++11 的 fqa 和 wikipedia 的例子都是「型別安全」的printf:

voidprintf(constchar*s)

else

} std::cout << *s++;

} }

template

voidprintf(constchar*s, t value, args... args)

else

} std::cout << *s++;

} throwstd::logic_error("extra arguments provided to printf");

} 這個例子確實很棒,夠短。僅僅x行的**中,就用到了選擇(if)和迴圈(while),遞迴(printf自身的遞迴),過載(cout的《對種輸出型別的過載),異常。這個例子不細講,大家試著用「上面的圖」來試試分析一下吧。(這個例子用來面試,讓面試者來解析下是不是不錯?……)

這裡有篇文章有一些關於這個特性加入 c++ 的動機,簡而言之,像 function ,bind 之類如果沒有這個特性,實現起來會非常的麻煩,而且這樣實現出來的**也會使得編譯時間變得很長。

你看,如果要支援39個引數,編譯時間會到700秒。而使用 variadic template 之後,**小了 40 k,同時各種引數個數的情況下編譯時間都小於 1 秒。

這很好。但是,這個特性用起來真的不是那麼容易,你也看到了。在和其它的特性一起使用的時候(多重繼承,右值引用)等在一起的時候,會更加地「複雜」。但它是乙個利器,用得好,會造出非常好用的庫。否則,我們就用庫好了,不要為去學習,記住和非要在日常程式設計中使用它而煩惱了。

variadic templates for gcc:

維基百科:

c++11當時的提案:n2080 (這個特別好,或是最好,我所見過的)

c 變長引數模板函式

c 中有乙個重要特性,那就是模板型別。類似於objective c中的泛型。c 通過類模板來實現泛型支援。類模板,可以定義相同的操作,擁有不同資料型別的成員屬性。通常使用template來宣告。告訴編譯器,碰到t不要報錯,表示一種泛型.如下,宣告乙個普通的類模板 template void foo ...

c 變長引數

c 在支援變長引數時,用到了三個巨集,va start va arg va end,和乙個資料型別va list。談談我對他們使用上理解吧 va list ptr 首先定義乙個該型別的指標 va start ptr,parm1 該型別指標和入參的第乙個引數關聯 type va arg ptr,tpy...

C 變長引數

如果c 的變長引數經過了多輪的呼叫,就可能失去作用 間接引址,但是只能引用到第乙個變長引數。va start marker,format s loggers filename loglinef format,va arg marker,va list va end marker va start m...