C++可變參數(shù)的函數(shù)與模板實(shí)例分析
本文實(shí)例展示了C++可變參數(shù)的函數(shù)與模板的實(shí)現(xiàn)方法,有助于大家更好的理解可變參數(shù)的函數(shù)與模板的應(yīng)用,具體內(nèi)容如下:
首先,所謂可變參數(shù)指的是函數(shù)的參數(shù)個(gè)數(shù)可變,參數(shù)類型不定的函數(shù)。為了編寫能處理不同數(shù)量實(shí)參的函數(shù),C++提供了兩種主要的方法:如果所有的實(shí)參類型相同,可以傳遞一個(gè)名為initializer_list的標(biāo)準(zhǔn)庫類型;如果實(shí)參的類型不同,我們可以編寫可變參數(shù)模板。另外,C++還有一種特殊的省略符形參,可以用它傳遞可變數(shù)量的實(shí)參,不過這種一般只用于與C函數(shù)交互的接口程序。
一、可變參數(shù)函數(shù)
1、initializer_list形參
如果函數(shù)的實(shí)參數(shù)量未知但是全部實(shí)參的類型都相同,我們可以使用initializer_list類型的形參(C++11新標(biāo)準(zhǔn))。和vector一樣,initializer_list也是一種模板類型。下面看看initializer_list提供的一些操作:
#include<initializer_list> // 頭文件 initializer_list<T> lst; // 默認(rèn)初始化,T類型元素的空列表 initializer_list<T> lst{a,b,c...}; // 初始化為初始值列表的副本 lst2(lst) // 拷貝或賦值不會(huì)拷貝列表中的元素;拷貝后, lst2 = lst // 原始列表和副本共享元素 lst.size() // 列表中的元素?cái)?shù)量 lst.begin() // 返回指向lst中首元素的指針 lst.end() // 返回指向lst中尾元素下一位置的指針
下面給出一個(gè)例子,需要注意的是,含有initializer_list形參的函數(shù)也可以同時(shí)擁有其他形參。另外,如果想給initializer_list形參傳遞一個(gè)實(shí)參的序列,必須把序列放在一對(duì)花括號(hào)內(nèi):
string func(initializer_list<string> li) { string str(""); for(auto beg=li.begin(); beg!=li.end(); ++beg) str += *beg; return str; } int main() { cout << func({"This"," ","is"," ","C++"}) << endl; return 0; }
2、省略符形參
函數(shù)可以用省略符形參”…“表示不定參數(shù)部分,省略符形參只能出現(xiàn)在形參列表的最后一個(gè)位置,它的形式如下:
void foo(parm_list, ...); // 典型例子 int printf(const char* format, ...)
省略符形參應(yīng)該僅僅用于C和C++通用的類型,因?yàn)榇蠖鄶?shù)類類型的對(duì)象在傳遞給省略符形參時(shí)都無法正確拷貝。下面是<cstdarg>頭文件中的幾個(gè)宏定義:
#include<cstdarg> // C中是<stdarg.h> // va_list是一種數(shù)據(jù)類型,args用于持有可變參數(shù)。 // 定義typedef char* va_list; va_list args; // 調(diào)用va_start并傳入兩個(gè)參數(shù):第一個(gè)參數(shù)為va_list類型的變量 // 第二個(gè)參數(shù)為"..."前最后一個(gè)參數(shù)名 // 將args初始化為指向第一個(gè)參數(shù)(可變參數(shù)列表) va_start(args, paramN); // 檢索參數(shù),va_arg的第一個(gè)參數(shù)是va_list變量,第二個(gè)參數(shù)指定返回值的類型 // 每一次調(diào)用va_arg會(huì)獲取當(dāng)前的參數(shù),并自動(dòng)更新指向下一個(gè)可變參數(shù)。 va_arg(args,type); // 釋放va_list變量 va_end(args);
下面給出一個(gè)例子:
int add_nums(int count,...) { int result = 0; va_list args; va_start(args, count); for(int i=0; i<count; ++i) result += va_arg(args, int); va_end(args); return result; } int main() { cout << add_nums(4, 25, 25, 50, 50) << endl; return 0; }
編譯器是將參數(shù)壓入棧中進(jìn)行傳遞的。傳遞實(shí)參的時(shí)候,編譯器會(huì)從實(shí)參列表中,按從右到左的順序?qū)?shù)入棧,對(duì)于add_nums(4, 25, 25, 50, 50)的調(diào)用,則入棧的順序是 50, 50, 25, 25, 4 (注意沒有可變參數(shù)與不可變參數(shù)之分)。由于棧的地址是從高到低的,所以在知道了第一個(gè)參數(shù)地址和參數(shù)的類型之后,就可以獲取各個(gè)參數(shù)的地址。
二、可變參數(shù)模板
一個(gè)可變參數(shù)模板(variadic template)就是一個(gè)接受可變數(shù)目參數(shù)的模板函數(shù)或模板類??勺償?shù)目的參數(shù)被稱為參數(shù)包(parameter packet)。存在兩種參數(shù)包:模板參數(shù)包(表示零個(gè)或多個(gè)模板參數(shù))和函數(shù)參數(shù)包(表示零個(gè)或多個(gè)函數(shù)參數(shù))。
上述說到我們可以使用一個(gè)initializer_list來定義一個(gè)可接受可變數(shù)目實(shí)參的函數(shù),但是所有實(shí)參必須具有相同的類型。當(dāng)我們既不知道要處理的實(shí)參數(shù)目也不知道它們的類型時(shí),我們就需要使用可變參數(shù)的函數(shù)模板了。我們用一個(gè)省略號(hào)來指出一個(gè)模板參數(shù)或函數(shù)參數(shù)表示一個(gè)包:在一個(gè)模板參數(shù)列表中,class...或typename...指出接下來的參數(shù)表示零個(gè)或多個(gè)類型的列表;一個(gè)類型名后面跟一個(gè)省略號(hào)表示零個(gè)或多個(gè)給定類型的非類型參數(shù)的列表。在函數(shù)參數(shù)列表中,如果一個(gè)參數(shù)的類型是一個(gè)模板參數(shù)包,則此參數(shù)也是一個(gè)函數(shù)參數(shù)包。
// Args是一個(gè)模板參數(shù)包;rest是一個(gè)函數(shù)參數(shù)包 template <typename T, typename...Args> void foo(const T &t, const Args&...rest);
可變參數(shù)函數(shù)模板通常是遞歸的。第一步調(diào)用處理包中的第一個(gè)實(shí)參,然后用剩余的實(shí)參調(diào)用自身。為了終止遞歸,我們還需要定義一個(gè)非可變參數(shù)的函數(shù)模板:
// 用來終止遞歸并處理包中最后一個(gè)元素 template <typename T> void print(const T &t) { cout << t; } // 包中除了最后一個(gè)元素之外的其他元素都會(huì)調(diào)用這個(gè)版本的print template <typename T, typename...Args> void print(const T &t, const Args&...rest) { cout << t << " "; // 打印第一個(gè)實(shí)參 print(rest...); // 遞歸調(diào)用,打印其他實(shí)參 } // 測(cè)試 int main() { print("string1", 2, 3.14f, "string2", 42); cout << endl; return 0; }
非可變參數(shù)版本的print負(fù)責(zé)終止遞歸并打印初始調(diào)用中的最后一個(gè)實(shí)參。對(duì)于最后一次遞歸調(diào)用print(42),兩個(gè)print版本都是可行的。但是,非可變參數(shù)模板比可變參數(shù)模板更特例化,因此編譯器選擇非可變參數(shù)版本。
相關(guān)文章
C++判斷一個(gè)點(diǎn)是否在圓內(nèi)的方法
這篇文章主要為大家詳細(xì)介紹了C++判斷一個(gè)點(diǎn)是否在圓內(nèi)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05利用OpenCV實(shí)現(xiàn)局部動(dòng)態(tài)閾值分割
這篇文章主要為大家詳細(xì)介紹了利用OpenCV實(shí)現(xiàn)局部動(dòng)態(tài)閾值分割,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01C++?Boost?ProgramOptions超詳細(xì)講解
Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱2022-11-11C語言實(shí)現(xiàn)點(diǎn)菜系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)點(diǎn)菜系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06基于Opencv實(shí)現(xiàn)顏色識(shí)別
這篇文章主要為大家詳細(xì)介紹了基于Opencv實(shí)現(xiàn)顏色識(shí)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07C語言實(shí)現(xiàn)注冊(cè)登錄系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)注冊(cè)登錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12