I am trying to understand the following code:
template <class...Ts, class...Us> void f(void) {};
int main() {
f<int, char, float, double>();
}
I don't know how template argument deduction deduces Ts
and Us
. In this answer, I learned that parameter packs are greedy, so Ts
will match all specified arguments: [ Ts = int, char, float, double ]
. But what about Us
: it has not been deduced yet? or what happens in this case?
I expect that the compiler throws a deduction error since it cannot figure out what Us
is expanded to.
Can I somehow, in the template argument-list, tell the compiler that I need Ts
to be int, char
, and Us
to be float, double
? how I can do that? I mean, can the call expression be something like that:
f<{int, char}, {float, double}>();
I found out that when I edit the above code to be as follows, this will fit my needs:
template <class...> struct S{};
template <class...Ts, class...Us> void g(S<Ts...>, S<Us...>)
{
};
int main() {
S<int, char> s1;
S<float, double> s2;
g(s1 ,s2);
}
But I still need to understand why in this case Ts
is [int, char]
, and Us
is [float, double]
.
What I think in this case is that: Since parameter packs are greedy, template argument deduction deduces Ts
as [S<int, char>, S<float, double>]
and Us
is left off un-deduced or just empty. Right?
This makes me think that
First,
[int, char]
gets substituted in the place ofTs...
in the first function parameterS<Ts..>
, so it gets expanded intoS<int, char>
. Then[float, double]
gets substituted in the place ofUs...
in the second function parameterS<Us..>
, so it gets expanded intoS<float, double>
.Second, template argument deduction deduces
Ts
as[int, char]
andUs
as[float, double]
.
Is my understanding correct in this case?
CodePudding user response:
But what about
Us
: it has not been deduced yet?
Us
is the empty sequence { }
or [ ]
, whichever notation you prefer to use.
Consider the following contrived example, to illustrate this:
template<typename... T>
void f(T... t)
{
}
int main() {
f(); // T is the empty sequence { }
}
What I think in this case is that: Since parameter packs are greedy, template argument deduction deduces
Ts
as[S<int, char>, S<float, double>]
andUs
is left off un-deduced or just empty. Right?
No, you're wrong in saying the above quoted statement because in the second snippet, the function template has two function parameters. The type of the first parameter is S<Ts...>
and the type of the second parameter is S<Us...>
.
This means that Ts
and Us
will be deduced using/from the template arguments of the passed S</*some args here*/>
and S</*some args here*/>
.
Now lets apply this to your second example. Since you've passed S<int, char>
and S<float, double>
as call arguments, the Ts
gets deduced to the sequence {int, char}
while the Us
gets deduced to the sequence {float, double}
.
Can I somehow, in the template argument-list, tell the compiler that I need Ts to be int, char, and Us to be float, double? how I can do that? I mean, can the call expression be something like that:
f<{int, char}, {float, double}>();
No, this is not possible because Ts
will consume all the passed template arguments such that Us
will be deduced to the empty sequence, in your first example.
CodePudding user response:
Regrading the second part of the question (second example).
Template argument deduction deduces parameter packs Ts...
as [int, char]
, and Us
as [ float, double ]
the same way it deduces T
if you have the following:
template <class> struct B {};
template <class T> void h(B<T>)
.. and you call h(B<int>())
, template argument deduction is applied as follows:
P = B<T>, A = B<int> -> [ T = int ] not [ T = B<int> ]
The same is applied in the first example:
P1 = S<Ts...>, A1 = S<int, char> --> [ Ts = int, char ];
P2 = S<Us...>, A2 = S<float, double> --> [ Us = float, double ];