目錄
1. 可變參函數模板基本介紹
2. 參數包展開——通過遞歸函數
3. 參數包展開——通過編譯期間if語句(constexpr if)
4. 重載
5. 后記
進來看的小伙伴們應該對C++中的模板有了一定了解,下面給大家介紹一下可變參函數模板。過于基礎的概念將不仔細介紹。
1. 可變參函數模板基本介紹
先來看一個可變參函數模板的示例:
#include <iostream>using namespace std;template<typename... T>
void myFunc(T... args)
{cout << "----------------MyFunc begin -----------------" << endl;cout << "收到的參數個數:" << sizeof...(args) << endl;cout << "收到的參數類型個數:" << sizeof...(T) << endl;cout << "----------------MyFunc end -----------------" << endl;
}int main()
{myFunc();myFunc(1);myFunc(1, 2);myFunc(1, 2, 3);
}
運行結果如下圖:
仔細觀察上面給出的代碼,有三點需要注意:
- template行中typename后面跟著三個點,然后是類型模板參數T
- 在函數簽名行void myFunc(T... args),因為T后面跟著三個點,所以T稱為可變參類型。看起來是一個類型,實際上其代表了0個到多個的類型。?args稱為一包參數,每個參數的類型都可以為任意類型。
- 在C++11引入了sizeof...()的語法,這是固定寫法,用于可變參函數模板或可變參模板內部,表示模板參數個數或類型數量。只有這種...的可變參才可以使用。
上面的代碼有一個問題,在拿到了一堆參數args之后,我們需要拿到每一個參數進行處理。這就涉及到了參數包的展開,下面將介紹兩種參數包展開的兩種方法。
2. 參數包展開——通過遞歸函數
參數包展開的套路是比較固定的,通過遞歸函數進行參數包展開是一種經典手法。
為了實現遞歸函數的方式展開參數包,需要編寫一個參數包展開函數和一個同名的遞歸終止函數。
這兩個函數一起實現參數包的展開。
先看代碼:
#include <iostream>using namespace std;void myFunc() // 同名的參數包遞歸終止函數
{cout << "參數包展開的遞歸終止函數" << endl;
}template<typename T, typename... U>
void myFunc(T arg1, U... args) // 參數包展開函數
{cout << "收到的參數為:" << arg1 << endl;myFunc(args...);
}int main()
{myFunc(1, 2, 3);
}
運行截圖如下圖所示:
通過代碼可以看到參數包展開函數是一個函數模板。void myFunc(T arg1, U... args);它的函數簽名中,T為1個參數,U為0個或多個參數類型。所以這個函數模板最少需要一個參數。
通過代碼可以看到,遞歸終止函數為一個同名的普通的函數(不是函數模板)接收0個參數。
在main函數中調用myFunc(1,2,3);時,1會匹配到arg1,2和3會匹配到args。
在myFunc內部會調用myFunc(2,3);,2會匹配到arg1,3會匹配到args。
myFunc內部繼續調用myFunc(3);,3會匹配到arg1,args沒有匹配到任何參數。
接著會調用myFunc(); 調用遞歸終止函數。
通過這種遞歸調用的方式就實現了參數包的展開,這是一種固定的編程手法,理解學會就好了。
3. 參數包展開——通過編譯期間if語句(constexpr if)
在C++17標準中增加了編譯期間if語句。與常規的if語句類似,但是在if語句后增加了一個constexpr關建字,這個關建字代表編譯時求值。
有了編譯期間if語句就非常方便了,不需要編寫遞歸終止函數了。具體看下面的代碼:
#include <iostream>using namespace std;template<typename T, typename ...U>
void myFunc(T arg1, U... args)
{cout << "收到的參數為:" << arg1 << endl;if constexpr (sizeof...(args) > 0){myFunc(args...);}else{cout << "沒有更多參數了" << endl;}
}int main()
{myFunc(1, 2, 3);
}
運行結果如下圖所示:
使用if constexpr和sizeof...()來判斷args中含有的參數個數。當剩余的參數個數大于0時,繼續遞歸調用,否則會執行else分支。
非常好理解,而且編寫起來也非常方便。
需要注意的是,if constexpr所指定的條件必須在編譯期間能夠求值,不可以是變量。
4. 重載
可變參函數模板也可以重載,具體見下面的代碼。
#include <iostream>using namespace std;template<typename... T>
void myFunc(T... args)
{cout << "void myFunc(T... args); 執行了" << endl;
}template<typename... T>
void myFunc(T*... args)
{cout << "void myFunc(T*... args); 執行了" << endl;
}void myFunc(int arg)
{cout << "void myFunc(int arg); 執行了" << endl;
}int main()
{myFunc(1, 2, 3);myFunc((int*)nullptr);myFunc(1);
}
運行截圖如下所示:
5. 后記
可變參函數模板還是比較好理解的,有講得不對的地方敬請批評指正。