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