Search code examples
c++templatesc++17language-lawyertemplate-meta-programming

Misunderstanding about non-deducible function template arguments


From C++ Templates - The Complete Guide 2nd edition:

Moreover, such parameters can't usefully be placed after a template parameter pack or appear in a partial specialization, because there would be no way to explicitly specify or deduce them.

template<typename ...Ts, int N>
void f(double (&)[N+1], Ts ... ps); // useless declaration because N
                                    // cannot be specified or deduced

where such parameters refers (I think) to the template parameters corresponding to those template arguments that can never be deduced. I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".

But why specifying it is not possible? I understand that it's not possible to specify N and let ...Ts be deduced, but why isn't it possible to specify them all? In other words, what is wrong in specifying Ts=[int] and N=2 via the following?

    double x[3];
    f<int,2>(x,1);

Solution

  • I.e. in the example above N is the parameter that cannot be deduced because N+1 is "too complicated to be deduced".

    Formally, this is [temp.deduct.type]/5.3

    The non-deduced contexts are:

    • [...]
    • /5.3 A non-type template argument or an array bound in which a subexpression references a template parameter.

    As is already covered in the following Q&A:

    particularly that in a template-head of a function template

    template<typename ...Ts, int N>
    // ... function template
    

    as per [temp.param]/14

    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 ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).

    specifically as per the special rule for function templates (due to function template argument deduction), the template argument N must be deducible from the function's parameter list. As per [temp.deduct.type]/5.3, it is not, and f in the following example can never invoked (overload resolution will never consider it a viable candidate):

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

    whereas the following functions can both be found by overload resolution:

    template<typename ...Ts, int N>
    void g(double (&)[N], Ts ... ps);   // N deducible from function parameter
    
    template<typename ...Ts, int N = 2> // N has a default-template-argument
    void h(double (&)[N+1], Ts ... ps);
    

    But why specifying it is not possible?

    As discussed in the linked to Q&A, although it "would make sense for a compiler to support this", the standard does not, and a leading template parameter pack greedily includes all explicitly provided template arguments, even those that would not be valid as part of the expanded pack (e.g. non-type template arguments to a type template parameter pack).