Let's say I have a variadic function template taking a function pointer to a function with said variadic arguments. The following code does not compile under gcc (11.2), but compiles under clang and msvc (https://godbolt.org/z/TWbEKWb9f).
#include <type_traits>
void dummyFunc(const int);
template<typename... Args>
void callFunc(void(*)(Args...), Args&&...); // <- this one is problematic
// see https://stackoverflow.com/questions/67081700/variadic-template-qualifiers
template<typename... Args>
void callFunc2(void(*)(std::conditional_t<std::is_const_v<Args>, const Args, Args>...), Args&&...); // <- this one works
int main()
{
// fails on gcc, works on clang and msvc
callFunc<const int>(&dummyFunc, 2);
// this works
//callFunc(&dummyFunc, 2);
// this works as well
//callFunc2<const int>(&dummyFunc, 2);
}
Specifying the function arguments 'Args...' explicitly as 'const int' prevents gcc from compiling the code. Apparently the template parameter expansion looses the cv-qualifier under gcc, while it is kept under clang and msvc. The error message is:
<source>: In function 'int main()':
<source>:15:24: error: no matching function for call to 'callFunc<const int>(void (*)(int), int)'
15 | callFunc<const int>(&dummyFunc, 2);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
<source>:6:6: note: candidate: 'template<class ... Args> void callFunc(void (*)(Args ...), Args&& ...)'
6 | void callFunc(void(*)(Args...), Args&&...);
| ^~~~~~~~
<source>:6:6: note: template argument deduction/substitution failed:
<source>:15:24: note: types 'const int' and 'int' have incompatible cv-qualifiers
15 | callFunc<const int>(&dummyFunc, 2);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
I found the 'solution' by utilizing type traits to make it compile here (Variadic template qualifiers), but I either should have to use this method or not, independently of the compiler. I know I don't have to specify the template arguments explicitly, but I want to understand why gcc fails to compile the code. Which compiler is "right", i.e. should this code compile or not (that is, should the cv-qualifier be dropped)?
As a side note, if I use a const reference instead, it compiles also under gcc, and the template argument is correctly recognized as 'const int&' (because the compiler absolutely has to do so). I am aware of the fact that, if the template argument is deduced by the compiler and not explicitly specified, that the cv-qualifier is dropped. But in my opinion gcc's behavior here is wrong because I explicitly stated the type to use. However, I don't know the standard well enough to tell whether gcc or the other two compilers are not following the standard. It seems to be related to variadic templates and not the template deduction itself, because the version without variadic templates works under gcc (https://godbolt.org/z/qn8a5bh5E):
void dummyFunc(const int);
template<typename Arg>
void callFunc(void(*)(Arg), Arg&&);
int main()
{
callFunc<const int>(dummyFunc, 2);
}
Also, why is automatic template type deduction even working in the first (problematic) case? 'Args...' should be deduced as both 'const int', because of the function pointer, as well as 'int' because of the second argument. My guess is that in this case, 'Args...' is deduced as 'const int' (otherwise it would not compile). Is my guess correct? It would be great if somebody could hint me to the relevant section in the standard.