I just naïvely wrote this:
#include <stdio.h>
template< class... Args >
auto format_for( Args... args, int last_arg )
-> char const*
{
// using Specifier = char const [3];
// static Specifier const s[] = { {'%', 'f'+(0*args), ' '}..., {'%', 'f', 0} };
return reinterpret_cast<char const*>( "" );
}
auto main() -> int
{
printf( "'%s'\n", format_for( 5, 2, 1 ) );
}
It crashes Visual C++ 2015 update 1, an ICE (Internal Compiler Error), and g++ 5.1.0 maintains that the function only takes one argument, presumably as a result of ignoring the Args
after failing to match this template parameter:
C:\my\forums\so\081> cl printf.cpp /Feb printf.cpp printf.cpp(14): fatal error C1001: An internal error has occurred in the compiler. (compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\cxx\dymto.c', line 6771) To work around this problem, try simplifying or changing the program near the locations listed above. Please choose the Technical Support command on the Visual C++ Help menu, or open the Technical Support help file for more information C:\my\forums\so\081> g++ printf.cpp printf.cpp: In function 'int main()': printf.cpp:14:43: error: no matching function for call to 'format_for(int, int, int)' printf( "'%s'\n", format_for( 5, 2, 1 ) ); ^ printf.cpp:4:6: note: candidate: template<class ... Args> const char* format_for(Args ..., int) auto format_for( Args... args, int last_arg ) ^ printf.cpp:4:6: note: template argument deduction/substitution failed: printf.cpp:14:43: note: candidate expects 1 argument, 3 provided printf( "'%s'\n", format_for( 5, 2, 1 ) ); ^ C:\my\forums\so\081> _
So,
Why does the above not compile with g++?
How can the intent (hopefully evident from the code) be expressed?
More generally, what are the rules for matching a call to declaration of variadic template function?
Args
appears in a non-deduced context. In this case, that makes it being deduced as the the empty pack.
If you want to extract the last argument but maintain usual deduction rules, 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 declaration of variadic template function?
Those are pretty verbose, but function parameter packs that are not trailing will generally either be deduced to empty ones or yield a deduction failure.
In your particular 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 that the compiler optimizes well - Demo 2. Or go down the cheeky 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...); // As before
return s;
}
template <class... Args>
auto format_for( Args&&... args ) {
return format_for(std::make_index_sequence<sizeof...(Args)>{},
std::forward<Args>(args)...);
}
Demo 3.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With