HCSF
HCSF

Reputation: 2649

Function Template Type Deduction Rules

I have:

template<typename ...Ts, typename U, typename=void>
void valid(Ts..., U){}

int main() {
    valid(1.0, 1, 2, 3);
}

clang complains:

note: candidate function [with Ts = <>, U = double, $2 = void] not viable: requires 1 argument, but 4 were provided
void valid(Ts..., U){}
     ^

And gcc complains:

<source>:2:6: note:   template argument deduction/substitution failed:
<source>:5:10: note:   candidate expects 1 argument, 4 provided
    5 |     valid(1.0, 1, 2, 3);
      |     ~~~~~^~~~~~~~~~~~~~

According to cling, it seems like the compiler deduces that Tn is an empty pack (i.e. <>). Why is that? I thought given the argument list (1.0, 1, 2, 3), U would be deduced to int and Tn... would be double, int, int. What are the rules here for the type deduction?

If I changes the caller to:

valid<double, int, int>(1.0, 1, 2, 3);

It works. However, if I change it to:

valid<double, int, int, int>(1.0, 1, 2, 3);

It fails:

<source>:2:6: note:   template argument deduction/substitution failed:
<source>:5:33: note:   candidate expects 5 arguments, 4 provided
    5 |     valid<double, int, int, int>(1.0, 1, 2, 3);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~

What rules are used here to determine what types I can specify in the type parameter list?

Thanks.

Upvotes: 0

Views: 87

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52471

First, why the template argument deduction fails when no template arguments are explicitly specified, as in valid(1.0, 1, 2, 3). This is simple - a parameter pack that's not trailing (not the last in the function parameter list) is non-deduced context:

[temp.deduct.type]/5 The non-deduced contexts are:
...
(5.7) — A function parameter pack that does not occur at the end of the parameter-declaration-list.


Second, how explicitly specified template arguments are matched to template parameters, in the presence of a parameter pack. The way it works is, once you get to an argument that corresponds to the pack, all subsequent arguments are taken to be part of that pack.

[temp.deduct.type]/9 ... If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by Pi.

Thus, valid<double, int, int>(1.0, 1, 2, 3) works because Ts... is taken to be three types {double, int, int}, and U is deduced from the fourth argument, 3. valid<double, int, int, int>(1.0, 1, 2, 3) doesn't work because Ts... is taken to be four types {double, int, int, int}, and then there's no function argument for the last parameter of type U.

Upvotes: 1

Related Questions