Args appears in a non-printable context. In this case, it is output as an empty packet .
If you want to extract the last argument, but support the usual rules of deduction, you can write a simple helper:
template <typename U> constexpr U&& last(U&& u) {return std::forward<U>(u);} template <typename U, typename... T> constexpr decltype(auto) last(U&&, T&&... t) {return last(std::forward<T>(t)...);}
Demo
More generally, what are the rules for matching a call to declaring a function of a variational pattern?
These are quite detailed, but packages of parameter parameters that are not finite, as a rule, are either empty or cause a failure.
In your specific case, try index_sequence s:
template <class... Args, std::size_t... indices> auto format_for( std::index_sequence<indices...>, Args... args ) { auto tup = std::forward_as_tuple(std::forward<Args>(args)...); using Specifier = char const [3]; static Specifier const s[] = { {'%', (char)('f'+(0*std::get<indices>(tup))), ' '}..., {'%', 'f', 0} }; int last_arg = std::get<sizeof...(Args)-1>(tup); return s; } template <class... Args> auto format_for( Args&&... args ) { return format_for(std::make_index_sequence<sizeof...(Args)-1>{}, std::forward<Args>(args)...); }
... and hope the compiler optimizes well - Demo 2 . Or go down the sassy road:
template <class... Args, std::size_t... indices> auto format_for( std::index_sequence<indices...>, Args... args ) { using Specifier = char const [3]; static Specifier const s[] = { {'%', (char)(indices == sizeof...(Args)-1? 'f' : 'f'+(0*args)), ' '}... }; int last_arg = last(args...);
Demo 3 .