C++11 可變引數模板
介紹
一個可變引數模板是一個接受可變數目引數的模板函式或模板類。可變數目的引數成為引數包。存在兩種引數包:模板引數包 ,表示零個或多個模板引數;函式引數包,表示零個或多個函式引數。
用一個省略號來指出一個模板引數或函式參數列示一個包。在模板引數列表中,class…或typename…指出接下來的參數列示零個或多個型別的列表;一個型別名後面跟著的省略號表示零個或多個該給定型別的非型別引數的列表。在函式引數列表中,如果一個引數的型別是一個模板引數包,則此引數也是一個函式引數包。例如:
//Args是一個模板引數包;rest是一個函式引數包
//Args表示零個或多個模板型別引數
//rest表示零個或多個函式引數
template<typename T,typename...Args>
void f(const T &t,const Args&...rest);
上例中的f是一個可變引數函式模板,有一個名為T的型別引數和一個名為Args的模板引數包。這個包表示零個或多個額外的型別引數。f的函式引數列表包含一個const&型別的引數,指向T的型別,還包含一個名為rest的函式引數包,此包表示零個或多個函式引數。
編譯器會從函式的實參推斷模板引數型別。對於一個可變引數模板,編譯器還會推斷包中引數的數目。例如對於下面給定的呼叫:
int i = 0; double d = 3.14; string s = "how are you";
f(i,s,42,d);
f(s,42,"hi");
f(d,s);
f("hi");
編譯器會例項化如下四個版本:
void f(const int&,const string&,const int&,const double&);
void f(const string&,const int&,const char[3]&);
void f(const double&,const string&);
void f(const char[3]&);
在每個例項中,T的第一個型別都從第一個實參型別推斷出來。剩下的實參提供函式額外實參的數目和型別。
sizeof…運算子
當需要知道包中有多少元素時,可以使用sizeof…運算子。類似sizeof,sizeof…也返回一個常量表示式,而且不會對其實參求值:
template<typename...Args>
void g(Args...args) {
cout << sizeof...(Args) << endl;//型別引數的數目
cout << sizeof...(args) << endl;//函式引數的數目
}
編寫可變引數函式模板
可變引數函式通常是遞迴的。第一步呼叫處理包中的第一個實參,然後用剩餘實參呼叫自身。如下的print函式,每次遞迴呼叫將第二個引數列印到第一個實參表示的流中。為了終止遞迴,還需要定義一個非可變引數的print函式,它接受一個流和一個物件:
//用來終止遞迴併列印最後一個元素的函式
//此函式必須在可變引數版本的print定義之前宣告
template<typename T>
void print(ostream& os, const T& t) {
os << t << endl;
}
//包中除最後一個元素之外的其他元素都會呼叫這個版本的print
template<typename T,typename...Args>
void print(ostream& os, const T& t, const Args&...rest) {
os << t << ' ';//列印第一個實參
print(os, rest...);//遞迴呼叫,列印其他實參
}
(在vs2019中,如果在作用域中沒有找到終止遞迴的函式不會導致無限遞迴,而是在編譯時直接報錯)
對於print(cout, i, s,d, 42);第二個函式的遞迴執行如下
呼叫 | t | rest |
---|---|---|
print(cout, i, s,d, 42); | i | s,d,42 |
print(cout, s,d, 42); | s | d,42 |
print(cout, d, 42); | d | 42 |
print(cout, 42); //呼叫非可變引數版本 |
相關文章
- c++可變模板引數C++
- C++ 可變引數模板遞迴展開C++遞迴
- 【Java】可變引數Java
- go-可變引數Go
- 可變引數例項
- C++反射機制:可變引數模板實現C++反射C++反射
- C++反射機制:可變引數模板實現C++反射薦C++反射
- C++反射機制:可變引數模板實現C++反射(二)C++反射
- 可變引數va_list
- Java方法05:可變引數Java
- Java - 可變引數的使用Java
- 【重學Java】可變引數Java
- Java的方法可變長引數Java
- 教你認識Java 可變引數Java
- Python可變引數*args和**kwargsPython
- Go函式接收可變引數Go函式
- C++逆向 可變引數HookC++Hook
- Java方法04:命令列傳遞引數、可變引數Java命令列
- C++11(列表初始化+變數型別推導+型別轉換+左右值概念、引用+完美轉發和萬能應用+定位new+可變引數模板+emplace介面)C++變數型別
- 遞迴函式,可變引數列表遞迴函式
- Java 的可變引數與 Collections 類Java
- C語言可變引數詳解C語言
- 淺談C#可變引數paramsC#
- PHP 函式可變數量的引數列表PHP函式變數
- 模板引數,模板分離編譯編譯
- python疑問5:位置引數,預設引數,可變引數,關鍵字引數,命名關鍵字引數區別Python
- golang學習之路之函式可變引數Golang函式
- java基礎(九) 可變引數列表介紹Java
- C語言怎麼實現可變引數C語言
- java 之泛型與可變引數詳解Java泛型
- c++ 模板模板引數("Template Template Parameters")C++
- Swift 呼叫 Objective-C 的可變引數函式SwiftObject函式
- Python 中的可變引數: 什麼是*args和**kwargs?Python
- 自己實現一個簡單可變引數函式函式
- php函式之如何用預設引數和可變長度引數方式傳遞?PHP函式
- 數值型模板引數的應用
- Bash變數和引數變數
- python變數和引數Python變數