14

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?

Community
  • 1
  • 1
Jamboree
  • 5,139
  • 2
  • 16
  • 36
  • Someone please help me learn a little here. I would have thought that `template void f(void(*)(nondeduced_t..., U)) {}` was ill-formed because a variadic parameter pack is appearing first. – AndyG Aug 23 '16 at 15:17
  • 1
    @AndyG from cppreference: [In a primary class template, the template parameter pack must be the final parameter in the template parameter list. In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments](http://en.cppreference.com/w/cpp/language/parameter_pack) – jaggedSpire Aug 23 '16 at 15:22
  • It appears that this is not clearly specified by the specccc – Johannes Schaub - litb Aug 23 '16 at 15:22
  • So your goal is for the function type to be deduced with the fixed `T...` passed in? This obviously works: `template struct outer { template void f( void(*)(Ts..., U ) ) {} };`. @JohannesSchaub-litb what isn't clear? The set of non-deduced contexts are *enumerated*, is this one of them? The standard describes how to deduce `U` (see `outer` above) assuming the first args are not deduced. – Yakk - Adam Nevraumont Aug 23 '16 at 15:38
  • 1
    I believe `T...` should be greedy under the standard -- it should leave nothing for `U` to consume, so there is no ambiguity. Right? – Yakk - Adam Nevraumont Aug 23 '16 at 15:39
  • 1
    Is [this behaviour](https://ideone.com/yjt6cI) normal? – BiagioF Aug 23 '16 at 16:04
  • @Yakk the spec says that "T..." is a nondeduced context (function parameter pack in nonfinal position) so it is skipped and U should be able to be deduced – Johannes Schaub - litb Aug 23 '16 at 17:05
  • @Yakk the second example here is unclear: http://coliru.stacked-crooked.com/a/99d961020057d746 – Johannes Schaub - litb Aug 23 '16 at 17:41
  • Where does the spec say that "NonDeducedContext ..." disables the "the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A" mechanism? As I read it till now, it would expand "T" with "nondeduced-context" and compare it against the argument. But it cannot deduce, yet has expanded the pack's size by one, which would cause deduction to fail. I can't find definitive wording. – Johannes Schaub - litb Aug 23 '16 at 17:43
  • The "something like below" code does not work with gcc on coliru BTW – Johannes Schaub - litb Aug 23 '16 at 17:52
  • @JohannesSchaub-litb It does work with gcc on coliru: http://coliru.stacked-crooked.com/a/f832f6286b587a98 – Jamboree Aug 24 '16 at 01:13
  • Ah, NVM I tested the U instead of the char version.. – Johannes Schaub - litb Aug 24 '16 at 07:59

1 Answers1

1

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);    
Jans
  • 11,064
  • 3
  • 37
  • 45
  • They are made non-deduced intentionally in the question. You can't answer the question by changing the prerequisite. – Jamboree Aug 24 '16 at 01:27