Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template arguments deduction for parameter type of function pointer involving non-deduced parameter pack

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?

like image 262
Jamboree Avatar asked Aug 23 '16 15:08

Jamboree


People also ask

What is template argument deduction?

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.

Can we use non-type parameters as argument templates?

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.

What are non-type parameters for templates?

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.

What is template argument in C++?

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.


1 Answers

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:

  1. 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.

  2. 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);    
like image 106
Jans Avatar answered Sep 21 '22 15:09

Jans