1.關於
本文略帶總結性,參考:泛化之美--C++11可變模版參數的妙用
參數包展開方式有兩種: 遞歸展開 和 逗號表達式展開。
本文代碼並非全部來自參考文章,自己做了注釋和修改。請以原文為准
2. 查詢參數包的參數個數
用下面的函數可以實現查詢參數包的個數
template<typename... Args>
unsigned int length(Args... args)
{
return sizeof...(args);
}
調用
length(1, "hello", "c++", "11"); // 返回4,4個參數
3.遞歸展開
3.1 注意, 遞歸展開需要考慮爆棧的情況。 說到這里,ubuntu(linux) 默認棧大小8M(使用命令 ulimit -a查看), Win下,Visual Studio 默認棧大小1M(連接器->系統)。
3.2 需要兩個函數:遞歸終止函數 和 遞歸函數
3.3 一個例子,輸出各個元素。函數參數為參數包的形式,使用遞歸展開.
// 1.遞歸終止函數
void print()
{
;
}
// 2.遞歸執行函數
template<typename T, typename... Args>
void print(T head, Args... args)
{
std::cout << head << (sizeof...(args) > 0 ? ", " : "");
print(args...);
}
3.4 調用及執行結果
print(1, "hello", "C++", 11);
3.5 其執行過程
print(1, "hello", "C++", 11);
print("hello", "C++", 11);
print("C++", 11);
print(11);
print();
例子中,遞歸終止到第0參數截止。根據執行過程可知,終止函數可以改為1個參數,最后一個函數僅僅作為終止遞歸使用,終止遞歸改為1個參數的函數還可以提高性能(減少函數的調用)。
3.6 遞歸終止改為一個參數
// 1.遞歸終止函數
template<typename T>
void print(const T& t)
{
std::cout << t << endl;
}
// 2.遞歸執行函數
template<typename T, typename... Args>
void print(T head, Args... args)
{
std::cout << head << (sizeof...(args) > 0 ? ", " : "");
print(args...);
}
同樣的,執行上面的調用代碼print(1, "hello", "C++", 11);
,其執行過程如下
print(1, "hello", "C++", 11);
print("hello", "C++", 11);
print("C++", 11);
print(11);
3.7 再看一個遞歸求和的例子, 同樣是使用參數包的形式
// 1.遞歸終止函數
template <typename T>
T sum(const T& t)
{
return t;
}
// 2.遞歸函數
template <typename T, typename ... Args>
T sum(const T& head, const Args... args)
{
return head + sum<T>(args...);
}
4.逗號表達式展開
逗號表達式?了解一下
4.1 回到上面print的例子,改為逗號表達式
// 1.用作輸出每個參數
template<typename T>
void show_arg(const T& t)
{
std::cout << t <<std::endl;
}
// 2.用作展開參數包
template <typename ... Args>
void expand(const Args&... args)
{
int arr[] = {(show_arg(args), 0)...}; /// (1)
}
代碼中,(1)的逗號表達式為: (show_arg(args), 0)。先執行表達式1(show_arg(args)),再執行表達式2(0)。 使用調用代碼expand(1, 2, 3, 4)
,(1)處的逗號表達式展開結果如下
int arr[] = { (show_arg(1), 0) , (show_arg(2), 0), (show_arg(3), 0), (show_arg(4), 0) };
而,第一個逗號表達式結果為0, 第二逗號表達式的結果為0, .... 最后一個逗號表達式展開后,逗號表達式的結果為0,所以(1)的代碼最后是這樣的:
int arr[] = {0, 0, 0, 0}; /// (2)
(2)的代碼聲明了一個int數組,數組有4個元素。 展開參數包的方式相當巧妙,比起遞歸,更加方便了。
4.2 使用lambda + 逗號表達式展開參數包
還是同樣的例子,輸出參數包元素。
template<typename Func, typename...Args>
void show_args(const Func &f, Args&... args)
{
// 初始化列表,列表的類型為int, std::initializer_list是c++11引入的模板
std::initializer_list<int>{(f(std::forward< Args>(args)), 0)...};
}
// 調用
show_args([](const int & item) {std:: cout << item << ", "; }, 1, 2, 3, 4); /// 輸出: 1, 2, 3, 4,