1

Why can't I explicitly specify d in following case?

#include <iostream>

template< typename a, typename b = int, typename ...c, typename d = int >
int
f(a, b, c..., d)
{
    return sizeof...(c);
}

int
main()
{
    std::cout << f< int, int, int, int/*, char*/ >(1, 2, 3, 4, 'd') << std::endl;
    return 0;
}

If I uncomment last template argument, then I expect output 2, but instead I get a hard error:

main.cpp:14:18: error: no matching function for call to 'f'
    std::cout << f< int, int, int, int, char >(1, 2, 3, 4, 'd') << std::endl;
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:6:1: note: candidate function not viable: requires 6 arguments, but 5 were provided
f(a, b, c..., d)
^
1 error generated.

What is the rule to deny it in this case?

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • @DieterLücking I also suspect this, but what is the set of rules to deal with template parameter pack and defaulted parameters? – Tomilov Anatoliy Dec 21 '15 at 17:26
  • Another reason why variadic packs go at the end. – 101010 Dec 21 '15 at 17:28
  • @101010 Theoretically compiler can calculate amount of arguments of function and infer that `x = 5 - 1 - 1 - 1 == 2`, but actually don't do it. – Tomilov Anatoliy Dec 21 '15 at 17:30
  • As your question has already been more or less answered, I'd like to add that using a `std::tuple` is one workaround for what you're trying to do: `int f(a, b, std::tuple, d)` [Demo](http://coliru.stacked-crooked.com/a/e607801bc62328ee) – AndyG Dec 21 '15 at 18:31
  • @AndyG - I don't think that solves the problem. The pack would be correctly deduced if deduction was being used. What's going on though is that Orient is trying to specify template arguments and in that context `c` is going to eat up all the parameters supplied. For example I alter your demo a bit and get the problem back: http://coliru.stacked-crooked.com/a/e94e7134b0baa078 – Edward Strange Dec 21 '15 at 18:45
  • @CrazyEddie: Yeah for sure. That's why I took out the template arguments :-) It's a workaround, not a solution. – AndyG Dec 21 '15 at 18:47
  • @AndyG - but the original code would work without template arguments as well. When being deduced the template system would figure out that `d` is supposed to take on the `char` type and that `c...` is everything before. – Edward Strange Dec 21 '15 at 19:09
  • @CrazyEddie: I'm not sure what you mean. [Here](http://coliru.stacked-crooked.com/a/42ec40468b40b96f) is the original code without template arguments. – AndyG Dec 21 '15 at 19:18

1 Answers1

6

Because packs are greedy. So char is actually part of c and you're expected to supply the argument associated with d, which is of type int due to the default.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • Hence `template< typename ...A, typename T > void f(A..., T) { return; }` is totally wrong construction, but allowed by the language rules, isn't it? – Tomilov Anatoliy Dec 28 '15 at 09:25