C++ variadic templates preserve the type and count of every argument. C ellipsis functions erase that information, which shifts type interpretation and argument-count discipline to the caller. Prefer C++ packs for new typed interfaces.
Packs, Expansions, and Folds #
A template parameter pack and a function parameter pack may contain zero or more elements. A pack expansion applies a pattern once per element.
#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 preserves the value category deduced for each forwarding reference. It does not extend an argument’s lifetime, so a callee must still obey its ordinary reference and ownership contract.
Expansions occur in template argument lists, function arguments, base-specifier lists, initializers, lambda captures, and other grammar-defined contexts. Nested or simultaneous expansions require packs with compatible lengths.
A unary fold, such as (values + ...), requires a nonempty pack unless the operator has a language-defined empty identity. Supply an explicit identity with a binary fold when empty input is meaningful; reject empty input with a constraint when it is not.
template <class... Ts>
requires (sizeof...(Ts) > 0)
auto product(Ts... values) {
return (... * values);
}C Variadic Functions #
A C function declared with ... reads unnamed arguments through va_list, va_start, va_arg, va_copy, and va_end. The declaration supplies neither count nor type metadata. A count parameter, format string, or documented sentinel is therefore part of the API contract.
#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;
}Default argument promotions apply across ...: float becomes double, and integer types narrower than int promote to int or unsigned int. va_arg must request the promoted, compatible type expected at that position. Reading the wrong type, omitting va_end, reading past the supplied arguments, or relying on an absent sentinel has undefined behavior. Use C varargs only for ABI compatibility or interfaces such as formatting that already impose a precise convention.
Comments
Read or comment on GitHub (sign-in required to comment)