C++ 可變參數樣板會保留每個引數的型別與數量。C 的省略號函式會抹除這些資訊,因而將型別解讀與引數數量的規範責任轉移給呼叫端。對於新的具型別介面,優先使用 C++ 參數包。
參數包、展開與摺疊 #
樣板參數包與函式參數包可以包含零個或多個元素。參數包展開會針對每個元素套用一次模式。
cpp
#include <utility>
template <class Function, class... Ts>
decltype(auto) invoke(Function&& function, Ts&&... values) {
return std::forward<Function>(function)(std::forward<Ts>(values)...);
}
template <class... Ts>
auto sum(Ts... values) {
return (0 + ... + values); // binary left fold; 0 is the empty-pack identity
}std::forward 會保留為每個轉送參考推導出的值類別。它不會延長引數的生命週期,因此被呼叫函式仍必須遵守一般的參考與所有權契約。
展開會出現在樣板引數列表、函式引數、基底指定器列表、初始化式、lambda 擷取,以及其他由文法定義的情境中。巢狀或同時進行的展開需要長度相容的參數包。
一元摺疊,例如 (values + ...),除非運算子具有語言定義的空集合單位元,否則需要非空參數包。當空輸入具有意義時,使用二元摺疊提供明確的單位元;當空輸入沒有意義時,使用約束拒絕空輸入。
cpp
template <class... Ts>
requires (sizeof...(Ts) > 0)
auto product(Ts... values) {
return (... * values);
}C 可變參數函式 #
以 ... 宣告的 C 函式會透過 va_list、va_start、va_arg、va_copy 與 va_end 讀取未命名引數。宣告不會提供數量或型別中繼資料。因此,數量參數、格式字串或文件化的哨兵值都是 API 契約的一部分。
c
#include <stddef.h>
#include <stdarg.h>
int sum_ints(size_t count, ...) {
va_list arguments;
va_start(arguments, count);
int result = 0;
for (size_t i = 0; i < count; ++i)
result += va_arg(arguments, int);
va_end(arguments);
return result;
}預設引數提升會套用於 ...:float 會變成 double,而窄於 int 的整數型別會提升為 int 或 unsigned int。va_arg 必須要求該位置所預期的提升後相容型別。讀取錯誤的型別、省略 va_end、讀取超過所提供的引數,或依賴不存在的哨兵值,都會產生未定義行為。只有在 ABI 相容性或格式化等已經規定精確慣例的介面中,才使用 C 可變參數。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)