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 上阅读或评论(发表评论需要登录)