C++可變參數的函數與模板實例剖析。本站提示廣大學習愛好者:(C++可變參數的函數與模板實例剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是C++可變參數的函數與模板實例剖析正文
本文實例展現了C++可變參數的函數與模板的完成辦法,有助於年夜家更好的懂得可變參數的函數與模板的運用,詳細內容以下:
起首,所謂可變參數指的是函數的參數個數可變,參數類型不定的函數。為了編寫能處置分歧數目實參的函數,C++供給了兩種重要的辦法:假如一切的實參類型雷同,可以傳遞一個名為initializer_list的尺度庫類型;假如實參的類型分歧,我們可以編寫可變參數模板。別的,C++還有一種特別的省略符形參,可以用它傳遞可變數目的實參,不外這類普通只用於與C函數交互的接口法式。
1、可變參數函數
1、initializer_list形參
假如函數的實參數目未知然則全體實參的類型都雷同,我們可使用initializer_list類型的形參(C++11新尺度)。和vector一樣,initializer_list也是一種模板類型。上面看看initializer_list供給的一些操作:
#include<initializer_list> // 頭文件 initializer_list<T> lst; // 默許初始化,T類型元素的空列表 initializer_list<T> lst{a,b,c...}; // 初始化為初始值列表的正本 lst2(lst) // 拷貝或賦值不會拷貝列表中的元素;拷貝後, lst2 = lst // 原始列表和正本同享元素 lst.size() // 列表中的元素數目 lst.begin() // 前往指向lst中首元素的指針 lst.end() // 前往指向lst中尾元素下一名置的指針
上面給出一個例子,須要留意的是,含有initializer_list形參的函數也能夠同時具有其他形參。別的,假如想給initializer_list形參傳遞一個實參的序列,必需把序列放在一對花括號內:
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、省略符形參
函數可以用省略符形參”…“表現不定參數部門,省略符形參只能湧現在形參列表的最初一個地位,它的情勢以下:
void foo(parm_list, ...); // 典范例子 int printf(const char* format, ...)
省略符形參應當僅僅用於C和C++通用的類型,由於年夜多半類類型的對象在傳遞給省略符形參時都沒法准確拷貝。上面是<cstdarg>頭文件中的幾個宏界說:
#include<cstdarg> // C中是<stdarg.h> // va_list是一種數據類型,args用於持有可變參數。 // 界說typedef char* va_list; va_list args; // 挪用va_start並傳入兩個參數:第一個參數為va_list類型的變量 // 第二個參數為"..."前最初一個參數名 // 將args初始化為指向第一個參數(可變參數列表) va_start(args, paramN); // 檢索參數,va_arg的第一個參數是va_list變量,第二個參數指定前往值的類型 // 每次挪用va_arg會獲得以後的參數,並主動更新指向下一個可變參數。 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; }
編譯器是將參數壓入棧中停止傳遞的。傳遞實參的時刻,編譯器會從實參列表中,按從右到左的次序將參數入棧,關於add_nums(4, 25, 25, 50, 50)的挪用,則入棧的次序是 50, 50, 25, 25, 4 (留意沒有可變參數與弗成變參數之分)。因為棧的地址是從高到低的,所以在曉得了第一個參數地址和參數的類型以後,便可以獲得各個參數的地址。
2、可變參數模板
一個可變參數模板(variadic template)就是一個接收可變數量參數的模板函數或模板類。可變數量的參數被稱為參數包(parameter packet)。存在兩種參數包:模板參數包(表現零個或多個模板參數)和函數參數包(表現零個或多個函數參數)。
上陳述到我們可使用一個initializer_list來界說一個可接收可變數量實參的函數,然則一切實參必需具有雷同的類型。當我們既不曉得要處置的實參數量也不曉得它們的類型時,我們就須要應用可變參數的函數模板了。我們用一個省略號來指出一個模板參數或函數參數表現一個包:在一個模板參數列表中,class...或typename...指出接上去的參數表現零個或多個類型的列表;一個類型名前面跟一個省略號表現零個或多個給定類型的非類型參數的列表。在函數參數列表中,假如一個參數的類型是一個模板參數包,則此參數也是一個函數參數包。
// Args是一個模板參數包;rest是一個函數參數包 template <typename T, typename...Args> void foo(const T &t, const Args&...rest);
可變參數函數模板平日是遞歸的。第一步驟用途理包中的第一個實參,然後用殘剩的實參挪用本身。為了終止遞歸,我們還須要界說一個非可變參數的函數模板:
// 用來終止遞合並處置包中最初一個元素 template <typename T> void print(const T &t) { cout << t; } // 包中除最初一個元素以外的其他元素都邑挪用這個版本的print template <typename T, typename...Args> void print(const T &t, const Args&...rest) { cout << t << " "; // 打印第一個實參 print(rest...); // 遞歸挪用,打印其他實參 } // 測試 int main() { print("string1", 2, 3.14f, "string2", 42); cout << endl; return 0; }
非可變參數版本的print擔任終止遞合並打印初始挪用中的最初一個實參。關於最初一次遞歸挪用print(42),兩個print版本都是可行的。然則,非可變參數模板比可變參數模板更特例化,是以編譯器選擇非可變參數版本。