Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to explicitly specify template arguments for multiple parameter packs [duplicate]

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

  1. First, [int, char] gets substituted in the place of Ts... in the first function parameter S<Ts..>, so it gets expanded into S<int, char>. Then [float, double] gets substituted in the place of Us... in the second function parameter S<Us..>, so it gets expanded into S<float, double>.

  2. Second, template argument deduction deduces Ts as [int, char] and Us as [float, double].

Is my understanding correct in this case?

like image 449
mada Avatar asked Oct 26 '25 11:10

mada


2 Answers

But what about Us: it has not been deduced yet?

The first snippet is ill-formed 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]).

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

(emphasis mine)

Note that MSVC correctly rejects the first example which can be seen here.

There is a gcc bug report submitted as:

The template parameter pack of a function template should be the last template parameter.


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?

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, first note that the first snippet is ill-formed.

like image 126
Anoop Rana Avatar answered Oct 28 '25 02:10

Anoop Rana


Regarding 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 second example:

P1 = S<Ts...>, A1 = S<int, char> --> [ Ts = int, char ];
P2 = S<Us...>, A2 = S<float, double> --> [ Us = float, double ];
like image 21
mada Avatar answered Oct 28 '25 01:10

mada