天天看點

C++ 11中的可變模闆參數

介紹C++11中的可變參數模闆和一些相關的模闆知識

目錄

​​概述​​

​​前言​​

​可變模闆參數的文法​

​​函數​​

​​類​​

​​非類型模闆參數和可變模闆參數結合​​

​函數參數包的展開​

​C++11中的展開方式​

​​遞歸展開​​

​​逗号表達式搭配initializer_list展開​​

​C++17中的折疊表達式​

​​一進制折疊表達式​​

​​二進制折疊表達式​​

​​如何評價​​

​類參數包的展開​

概述

大家在C++中應該見過不少函數,它們既沒有限制參數的類型,也沒有限制參數的個數,比如​<code>​vector&lt;T&gt;::emplace()​</code>​,​<code>​make_unique&lt;T&gt;()​</code>​。它們都是利用C++11中的可變模闆參數來實作的。對于這一新特性,需要掌握以下三點

可變模闆參數的文法

參數包的展開

實踐

前言

在講可變模闆參數之前,需要先講C語言中的變長參數

C語言中的​<code>​va_arg​</code>​宏并不能判斷出哪個參數參數包的末尾,是以隻能通過自己設定結束位,并通過顯式判斷來截取有效的參數

而在C++11中,使用變長參數更簡單了(好吧其實也不怎麼算變長)

以C++11的标準來看,聲明一個可變參數的模闆函數有兩種方法

函數參數包的展開

假設我們通過設計一個函數,能逐個輸出它的參數

以上代碼的遞歸過程為

​<code>​print(1, 2, "Mike", 3.21);​</code>​

​<code>​print(2, "Mike", 3.21);​</code>​

​<code>​print("Mike", 3.21);​</code>​

​<code>​print(3.21);​</code>​

​<code>​print();​</code>​

C++ 11中的可變模闆參數

通過遞歸方式展開參數包,當所有參數包展開完畢後,自然為空,是以調用到非模闆的遞歸中止函數

當然還可以使用模闆遞歸中止函數,這種情況就不支援空參數包了

而在C++17中,對遞歸展開法進行了優化(前提是将​<code>​if​</code>​語句聲明為常量表達式)

在C++11中通過遞歸展開參數包的缺點很明顯,需要重載一個遞歸終止函數,同時還需要判定終止函數是否需要使用到模闆;不僅如此,還需要確定帶參數包版本的函數至少包含一個類型(​<code>​T fistst​</code>​),可以說是很不便了。下面介紹逗号表達式結合​<code>​initializer_list​</code>​的展開方法

這種搭配​<code>​initializer_list​</code>​的解法我願稱之為黑魔法,在構造初始化清單的同時完成了參數包的展開

以下示範一個求和模闆函數,如果我們直接使用參數包進行操作而不展開它,那麼我們将會得到報錯

C++ 11中的可變模闆參數

先來看看如何在C++17中利用折疊表達式​<code>​(...)​</code>​展開參數包實作一個求平均數的函數

對于一進制折疊表達式而言,隻有逗号運算符,​<code>​&amp;&amp;​</code>​和​<code>​||​</code>​操作允許空包,其它的如果出現空包則會編譯出錯

When a unary fold is used with a pack expansion of length zero, only the following operators are allowed: Logical AND (&amp;&amp;). The value for the empty pack is true Logical OR (||). The value for the empty pack is false The comma operator (,). The value for the empty pack is void()

二進制折疊表達式,支援空包操作

對于​<code>​std::cout​</code>​的二進制表達式而言,隻能使用左折疊(因為輸出必須以​<code>​std::cout​</code>​開頭,而這也就代表了它是左折疊)

拓展:以下代碼是在參數包展開完畢之後再輸出​<code>​std::endl​</code>​,而不是每拆一次就輸出一次

C++ 11中的可變模闆參數
​​Fold expressions with arbitrary callable?​​

類參數包的展開

C++11中可以使用遞歸或逗号表達式來展開函數參數包,C++17中可以使用優化的遞歸或折疊表達式來展開。

C++11中的類參數包的展開需要運用到類模闆的偏特化

此展開方式不支援0參數包,是以可以改寫為以下方式

繼續閱讀