This is similar to the question, but a more specific case. This time, no compiler work as expected.
template<class T>
struct nondeduced
{
using type = T;
};
template<class T>
using nondeduced_t = typename nondeduced<T>::type;
template<class... T, class U>
void f(void(*)(nondeduced_t<T>..., U)) {}
void g(int, char) { }
int main()
{
f<int>(g); // error?
}
In the above example, the parameter pack T
cannot be deduced, but the compiler should be able to deduce U
after explicit arguments substitution for pack T
(i.e. single int
in this case).
The above is expected to work without the nondeduced_t
trick as well:
template<class... T, class U>
void f(void(*)(T..., U)) {}
Because the parameter pack T
is already in non-deduced context according to
[temp.deduct.type]p5
The non-deduced contexts are:
- A function parameter pack that does not occur at the end of the parameter-declaration-list.
Unfortunately, no compiler I tested (g++/clang) accept the code. Notably something like below works on both g++ & clang.
template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
And again, this doesn't work on both:
template<class... T>
void f(void(*)(T..., char)) {}
Is my expectation wrong?
Template argument deduction is used when selecting user-defined conversion function template arguments. A is the type that is required as the result of the conversion. P is the return type of the conversion function template.
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.
A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
By [temp.deduct.type]p5 one of the non-deduced-context is
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Parameter packs that doesn't appear as the last argument of a template functions are never deduced, but is completely right to specify the parameter types disabling the deduction. e.g
template<class T1, class ... Types> void g1(Types ..., T1);
g1<int, int, int>(1,2,3); // works by non-deduction
g1(1,2,3) // violate the rule above by non-deduced context
But changing the order of function argument even leaving the template parameters as they are, remove the non-deduced context condition and break the infinite expansion of parameter pack. e.g
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3) // works because its a deduced context.
There're two reasons your code don't compile:
The order of function argument create a non-deduced-context which cause the type of the parameter pack T in the pattern stated in function f would never be deduced.
The template parameter T appears only as a qualifiers in function arguments(e.g nondeduced_t) and not directly specified as a function argument(which allow argument deduction).
To make the code compile you have either place the expansion of the parameter pack as it is forgetting the nondeduced_t indirect, as
template<class... T,class U>
void f( void(*)(U,T...) ) { }
f(g);
or changing the order of template parameters and specify the template argument on function call, as
template<class U,class... T>
void f( void(*)(U,typename nondeduced<T>::type...) ) {}
f<int,char>(g);
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