13

I am reading C++ Templates (2nd edition) and this is a snippet from the book:

template<typename... Ts, int N>
void f(double (&)[N+1], Ts... ps) {return;}

It is specified in the book that the declaration above is useless because N cannot be specified or deduced.

I am trying to understand why something like the following is an error:

double arr[2];
f<int, double, 1>(arr, 1, 2.0);

When I compile the snippet above, I get the error that there is no matching function for call to f.

This compiles fine

template<typename... Ts, typename T> 

void func(T value){}; 
func(1); 

even though I have an additional parameter after a parameter pack.

Why does my specifying of the template arguments explicitly not match the arguments provided? Please help me understand this.

MSS
  • 553
  • 2
  • 11
  • @πάντα ῥεῖ - Hmmm...This compiles fine - template void func(T value){}; func(1); even though I have an additional parameter after a parameter pack. Once again, my question here is different from the one you have linked to as I am not asking the compiler to do the deduction but I am helping it with the deduction. Need to understand why that fails. – MSS Dec 15 '18 at 08:30
  • Clang gives this notice "candidate template ignored: invalid explicitly-specified argument for template parameter 'Ts'". The invocation `f(arr, 1, 2.0)` compiles succesfully! So is `f(arr, 1, 2.0)`. So it seems `N` can be deduced, at least in g++ and clang++. – CygnusX1 Dec 15 '18 at 08:32
  • @CygnusX1....sorry my bad...i have edited the question....it is actually N+1 and not N.... – MSS Dec 15 '18 at 08:39

1 Answers1

14

The fact that N cannot be deduced has nothing to do with the parameter pack. This doesn't compile either because N cannot be deduced.

template<int N>
void f(double (&)[N+1]) {}

int main() {
    double arr[2];
    f(arr);
}

From cppreference (Non-deduced contexts):

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
[...]
3) A non-type template argument or an array bound in which a subexpression references a template parameter

The fact that N cannot be specified has a different cause: the standard says that you simply cannot specify a parameter placed after a parameter pack.

From [temp.param]:

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument (14.8.2). [Example:

template<class T1 = int, class T2> class B;    // error

// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { }  // error
template<class... T, class U> void g() { }     // error

—end example ]

(see this question from which I got the quote)

Your other example works because T can be deduced from the parameters.

template<typename... Ts, typename T> 
void func(T value){}; 
func(1); // 1 is used to deduce T
Nelfeal
  • 12,593
  • 1
  • 20
  • 39