最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++可變參數(shù)模板使用示例源碼解析

 更新時(shí)間:2023年01月13日 16:04:28   作者:一只修仙的猿  
這篇文章主要為大家介紹了c++可變參數(shù)模板使用示例源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

我們知道,C++模板能力很強(qiáng)大,比起Java泛型這種語(yǔ)法糖來(lái)說(shuō),簡(jiǎn)直就是降維打擊。而其中,可變參數(shù)模板,就是其中一個(gè)非常重要的特性。那什么是可變參數(shù)模板,以及為什么我們需要他?

首先我們考慮一個(gè)經(jīng)典的場(chǎng)景:

我們需要編寫一個(gè)函數(shù),來(lái)打印變量信息。

比如:

int code = 1;
string msg = "success";
printMsg(code,msg); // 輸出: 1,success

而我們需要打印的參數(shù)信息是不確定的,也有可能是下面的情況:

float value = 0.8f;
printMsg(code,msg,"main"); // 輸出: 1,success,main
printMsg(value,code); // 輸出: 0.8,1

printMsg的參數(shù)類型、數(shù)量都是不確定的,無(wú)論是普通模板、還是使用容器,都無(wú)法完成這個(gè)任務(wù)。而可變參數(shù)模板,可以非常完美完成這個(gè)任務(wù)。

可變參數(shù)模板,意為該模板的類型與數(shù)量都是不確定,能夠接收任意的參數(shù)匹配,造就了其極高的靈活度。

認(rèn)識(shí)可變模板參數(shù)

template<typename T,typename... Args>
void printMsg(T t, Args... args) {}

上述代碼為可變參數(shù)模板的例子。首先要了解一個(gè)概念:模板參數(shù)包,函數(shù)參數(shù)包

typename...表示一個(gè)模板參數(shù)包類型,在typename后跟了三個(gè)點(diǎn) ,Args是一個(gè)模板參數(shù)包,他可以是0或多種類型的組合。Args...,表示將這個(gè)參數(shù)包展開,作為函數(shù)的形參,args也稱為函數(shù)參數(shù)包

舉個(gè)例子:

// T的類型是 int
// Args的類型是 int、float、string 組成的模板參數(shù)包
printMsg(1,2,0.8f,"success");
// 模板會(huì)被實(shí)例化為此函數(shù)原型
void printMsg(int,int,float,string);

對(duì)于參數(shù)包,我們可以使用sizeof... 來(lái)獲取該參數(shù)包中有多少個(gè)類型。如sizeof...(args); or sizeof...(Args);。

那么,對(duì)于這個(gè)可變模板參數(shù)類型,我們要如何使用它呢?

使用可變模板參數(shù)

遞歸法

遞歸法利用的是類型匹配原理,將參數(shù)包中的參數(shù),一個(gè)個(gè)給他分離出來(lái)。我們從一個(gè)實(shí)際的例子來(lái)理解他。假如我們要實(shí)現(xiàn)前言章節(jié)中的printMsg函數(shù),那么他的實(shí)現(xiàn)代碼如下:

template<typename T,typename ...Args>
void printMsg(const T& t, const Args&... args) {
    std::cout << t << ", ";
    printMsg(args...);
}
// 調(diào)用
printMsg(1,0.3f,"success");

當(dāng)我們調(diào)用printMsg(1,0.3f,"success")代碼時(shí),模板函數(shù)被實(shí)例化為:

template<int,float,string>
void printMsg(const int& t, const float& arg1, const string& arg2) {
    std::cout << t << ", ";
    printMsg(arg1, arg2); 
}

代碼中再次遞歸調(diào)用了printMsg,模板函數(shù)被實(shí)例化為:

template<float,string>
void printMsg( const float& arg1, const string& arg2) {
    std::cout << t << ", ";
    printMsg(arg2); 
}

發(fā)現(xiàn)規(guī)律了嗎?當(dāng)我們不斷遞歸調(diào)用printMsg時(shí),參數(shù)報(bào)Args會(huì)被一層層解開,并將類型匹配到模板T上,從而將參數(shù)包Args中的參數(shù)逐一處理。

與此同時(shí),我們也知道一個(gè)關(guān)鍵點(diǎn):遞歸需要有終止條件。因此,我們需要在只剩下一個(gè)參數(shù)的時(shí)候?qū)⑵浣K結(jié):

template<typename T>
void printMsg(const T& t) {
    std::cout << t << std::endl;
}

c++在匹配模板時(shí),會(huì)優(yōu)先匹配非可變參數(shù)模板,因此非可變參數(shù)模板則成為了遞歸的終止條件。這樣我們就實(shí)現(xiàn)了一個(gè)函數(shù),能夠接受任意數(shù)量、任意類型(支持<<運(yùn)算符)的參數(shù)。

特例化

遞歸法是最為常見(jiàn)的使用可變參數(shù)模板的方式。對(duì)于參數(shù)包來(lái)說(shuō),除了遞歸法,其次就為特例化。舉個(gè)例子,還是我們上面的printMsg函數(shù):

template<>
void printMsg(const int& errorCode,const float& strength,const double& value) {
    std::cout << "errorCode:" << errorCode << " strength:" << strength << " value:" << value << std::endl;
}
printMsg(1,0.8f,0.8);

針對(duì)<int,float,double>類型的模板做了一個(gè)特例化,則在我們調(diào)用此類型的模板時(shí),會(huì)優(yōu)先匹配特例化。這也是一種處理可變模板參數(shù)的方式。

除此之外,還有很多對(duì)于可變模板參數(shù)的神奇用法,進(jìn)一步提高他的靈活性。

包拓展

這里包,指的是函數(shù)參數(shù)包以及可變模板參數(shù)包。前面的例子中已經(jīng)存在兩個(gè)包拓展,但更多的是屬于可變參數(shù)模板的語(yǔ)法層面,所以并沒(méi)有展開說(shuō)。比如上面我們提到的代碼:

template<typename T,typename ...Args>
void printMsg(const T& t, const Args&... args) {
    std::cout << t << ", ";
    printMsg(args...);
}
printMsg(1,0.8f,0.8);

這里有兩個(gè)包拓展:

  • 函數(shù)的形參,在Args& 之后跟了三個(gè)點(diǎn),表示將Args參數(shù)包展開,例子中展開后的函數(shù)原型是void printMsg(const int&,const float&,const double&);
  • 第二處展開是在遞歸調(diào)用時(shí),將函數(shù)參數(shù)包形參展開args...,例子中展開后為printMsg(0.8f,0.8);

在涉及到函數(shù)調(diào)用、函數(shù)聲明時(shí),都需要用到上面這兩個(gè)包拓展語(yǔ)法。但我們會(huì)發(fā)現(xiàn)并沒(méi)有什么可以操作的空間,他更多就是一個(gè)可變模板函數(shù)的固定語(yǔ)法。但除此之外,包拓展可以有一個(gè)更加神奇的操作。

還是上面的例子,但是這里我們需要對(duì)打印的數(shù)據(jù)進(jìn)行一輪過(guò)濾,對(duì)int數(shù)據(jù)超過(guò)99、float數(shù)據(jù)超過(guò)0.9進(jìn)行預(yù)警報(bào)告,其他數(shù)據(jù)不做處理。那么這個(gè)怎么處理呢?

理論上說(shuō),我們需要對(duì)每個(gè)參數(shù)包中的每個(gè)數(shù)據(jù)進(jìn)行處理,那我們可以在遞歸中,判斷T的類型,再根據(jù)不同的類型進(jìn)行處理。這種方式是可行的,但c++提供了更加好用的另一種方式??聪旅娴拇a:

template<typename T>
const T& filterParam(const T& t) { return t; }
template<>
const int& fileterParam(const int& t) {
    if (t > 99) { onWarnReport(); }
    return t;
}
template<>
const float& fileterParam(const float& t) {
    if (float > 0.9) { onWarnReport(); }
    return t;
}
template<typename... Args>
void printMsgPlug(const Args&... args) {
    printMsg(filterParam(args)...);  //關(guān)鍵代碼
}
printMsgPlus(1,0,3f,1.8f);

可以看到我們的關(guān)鍵代碼在于printMsg(filterParam(args)...);這一行,他等價(jià)于printMsg(filterParam(1),filterParam(0.3f) ,filterParam(1.8f)); 三個(gè)小點(diǎn)移動(dòng)到了函數(shù)調(diào)用的后面,即可以實(shí)現(xiàn)這樣的效果。

這種方式的優(yōu)點(diǎn)在于,他可以將過(guò)濾相關(guān)的邏輯,抽離到另外一個(gè)函數(shù)中去單獨(dú)處理,利用模板的特性對(duì)數(shù)據(jù)進(jìn)行統(tǒng)一或者單獨(dú)處理。而且,使用typeId判斷類型的方式并不總是可靠的,這種方式會(huì)更加穩(wěn)定。

此外,針對(duì)雙重過(guò)濾的方式,包拓展的解決方案也會(huì)更加優(yōu)雅。假如,我們?cè)诖蛴?shù)據(jù)之前,需要對(duì)數(shù)據(jù)進(jìn)行一次轉(zhuǎn)換,之后再對(duì)轉(zhuǎn)換結(jié)果進(jìn)行過(guò)濾判斷是否需要預(yù)警報(bào)告。那么我們的偽代碼可以是如下:

template<typename T>
T filterParam(const T& t) {
    T result = convertParam(t);
    if()...
    return result;
}
template<typename T>
T convertParam(const T& t) {...}
template<typename... Args>
void printMsgPlug(const Args&... args) {
    printMsg(filterParam(args)...);  //關(guān)鍵代碼
}

而如果使用遞歸結(jié)合typeid的方式,可能就需要更多個(gè)switch進(jìn)行類型匹配嵌套解決,且其結(jié)果總是不可靠的。

最后,并不是所有可變模板函數(shù),都能使用遞歸去解決問(wèn)題。例如我們需要一個(gè)能夠構(gòu)建unique_ptr的函數(shù),他的簡(jiǎn)化版可以是這樣的:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&... args) {
    return std::unique_ptr<T>(new T(fileterParam(args)...));
}

這個(gè)寫法是不夠完善的,但是方便我們理解。這個(gè)時(shí)候,如果我們需要對(duì)參數(shù)進(jìn)行過(guò)濾,那么遞歸的方式,就無(wú)法在這里使用了,而必須使用包拓展。

完美轉(zhuǎn)發(fā)

完美轉(zhuǎn)發(fā)在可變模板中非常常見(jiàn),他的作用在于保持原始的數(shù)據(jù)類型。參考我們上面的make_unique函數(shù),在移除fileterParam函數(shù)之后,,我們希望,傳給make_unique函數(shù)的數(shù)據(jù),能夠原封不動(dòng)地,傳遞給T的構(gòu)造函數(shù)。那么他的實(shí)現(xiàn)如下:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
  • Args&& 表示通用引用,他能接收左值引用,也可以接收右值引用。
  • std::forward 表示保持參數(shù)的原始類型。因?yàn)槲覀冎?,右值引用本身是左值,所以我們需要將其轉(zhuǎn)為右值傳遞給構(gòu)造函數(shù)。

這樣,我們就能夠原封不動(dòng)地將數(shù)據(jù)傳遞給構(gòu)造函數(shù),而不修改數(shù)據(jù)類型。這部分類型屬于右值與引用的范疇,這里不詳細(xì)展開解析。

但是對(duì)于可變模板來(lái)說(shuō),這里有一個(gè)關(guān)鍵需要注意一下:通用引用的本身,是 引用類型。假如我們傳遞了一個(gè)int類型進(jìn)來(lái),那么轉(zhuǎn)化之后就變成了int&。此時(shí)如果我們使用Args類型去做模板匹配,很容易發(fā)生匹配失敗的問(wèn)題,會(huì)提示int&無(wú)法匹配到int類型,需要多加注意一下。要解決這個(gè)問(wèn)題也比較簡(jiǎn)單,將其引用類型移除即可。在c++11中,可以使用以下代碼移除所有的修飾與引用,保持基礎(chǔ)的數(shù)據(jù)類型:

template<typename T>
using remove_cvRef = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
std::vector<decltype(remove_cvRef<T>)> v;

在匹配模板的時(shí)候,可以使用decltype來(lái)獲取移除后的類型進(jìn)行匹配。

總結(jié)

可變參數(shù)模板在實(shí)際的使用中,更多還是結(jié)合完美轉(zhuǎn)發(fā)來(lái)使用,實(shí)現(xiàn)對(duì)象的統(tǒng)一構(gòu)造或者接口調(diào)用封裝等。可變參數(shù)的存在,使得模板接口的靈活度提升了一個(gè)檔次,如果你在實(shí)際開發(fā)中遇到類似的需求,不妨使用一下,會(huì)給你帶來(lái)驚喜的。

以上就是c++可變參數(shù)模板使用示例源碼解析的詳細(xì)內(nèi)容,更多關(guān)于c++可變參數(shù)模板的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++中浮點(diǎn)數(shù)、double類型如何與0值作比較詳解

    C++中浮點(diǎn)數(shù)、double類型如何與0值作比較詳解

    浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)機(jī)制和整型數(shù)不同,其有舍入誤差,在計(jì)算機(jī)中用近似表示任意某個(gè)實(shí)數(shù),這篇文章主要介紹了C++中浮點(diǎn)數(shù)、double類型如何與0值作比較的相關(guān)資料,需要的朋友可以參考下
    2025-03-03
  • C/C++中命名空間(namespace)詳解及其作用介紹

    C/C++中命名空間(namespace)詳解及其作用介紹

    今天小編就為大家分享一篇關(guān)于C++命名空間namespace的介紹與使用,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2021-09-09
  • C++利用鏈棧實(shí)現(xiàn)表達(dá)式求值

    C++利用鏈棧實(shí)現(xiàn)表達(dá)式求值

    這篇文章主要為大家詳細(xì)介紹了C++利用鏈棧實(shí)現(xiàn)表達(dá)式求值的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Easyx實(shí)現(xiàn)窗口自動(dòng)碰撞的小球

    Easyx實(shí)現(xiàn)窗口自動(dòng)碰撞的小球

    這篇文章主要為大家詳細(xì)介紹了Easyx實(shí)現(xiàn)窗口自動(dòng)碰撞的小球,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C++將模板實(shí)現(xiàn)放入頭文件原理解析

    C++將模板實(shí)現(xiàn)放入頭文件原理解析

    這篇文章主要為大家介紹了C++將模板實(shí)現(xiàn)放入頭文件原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 使用C語(yǔ)言實(shí)現(xiàn)三子棋游戲

    使用C語(yǔ)言實(shí)現(xiàn)三子棋游戲

    這篇文章主要為大家詳細(xì)介紹了使用C語(yǔ)言實(shí)現(xiàn)三子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 使用C++制作GC Server過(guò)程詳解

    使用C++制作GC Server過(guò)程詳解

    這篇文章主要介紹了使用C++制作GC Server過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • C++關(guān)鍵字之likely和unlikely詳解

    C++關(guān)鍵字之likely和unlikely詳解

    這篇文章主要介紹了C++關(guān)鍵字之likely和unlikely,C++20之前的,likely和unlikely只不過(guò)是一對(duì)自定義的宏,而C++20中正式將likely和unlikely確定為屬性關(guān)鍵字,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2022-10-10
  • C++?基本數(shù)據(jù)類型中int、long等整數(shù)類型取值范圍及原理分析

    C++?基本數(shù)據(jù)類型中int、long等整數(shù)類型取值范圍及原理分析

    這篇文章主要介紹了C++?基本數(shù)據(jù)類型中int、long等整數(shù)類型取值范圍及原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 《C++ primer plus》讀書筆記(一)

    《C++ primer plus》讀書筆記(一)

    本讀書筆記是C++ primer plus(第六版)前3章的學(xué)習(xí)筆記。非常的淺顯易懂,復(fù)習(xí)C++基礎(chǔ)知識(shí)的可以瞄瞄。
    2014-10-10

最新評(píng)論

石棉县| 伽师县| 崇礼县| 四子王旗| 香河县| 云浮市| 龙胜| 墨玉县| 禹州市| 沈阳市| 彭山县| 柳河县| 闻喜县| 无极县| 芜湖市| 哈尔滨市| 台江县| 虹口区| 松滋市| 霍州市| 华容县| 五寨县| 前郭尔| 莎车县| 北川| 深圳市| 长乐市| 丘北县| 巴彦淖尔市| 永宁县| 军事| 龙泉市| 阿拉善盟| 大宁县| 塘沽区| 读书| 东乡族自治县| 枣庄市| 嘉祥县| 甘孜县| 增城市|