T.100 Suggested Example
template<typename... T>
void print_all(const T& ...args) {
(output(args), ...);
}
print_all(a, 0, string{"xyz"}); // good, type-safe
printf("%d %d %s", a, 0, "xyz"); // bad, UB if %d the wrong specifier for a,
// UB if we use std::string
I think this example demonstrates nicely how variadic functions are a "just works" solution and what safety issues arise with printf by comparison.
could even make it complete with (std::cout << ... << args); except it doesn't space-separate..
((std::cout << args << ' '), ...);
would be closer, but this isn't so nice-looking. If you wanted to avoid a trailing space, it would get even worse, because you would need a conditional operator and an index outside the fold expression:
size_t i = 0;
((std::cout << args << (i == 0 ? "" : " ")), ...);
That's why I think it's for the best to keep it at an abstract output function.
Printf has its purpose with embedding output formatting in meaning and easy mechanisms.
This may work as a replacement for variadic arguments for some cases, but printf is not one of them.