WEBVTT

00:00.090 --> 00:00.630
Hello again!

00:00.900 --> 00:03.990
In this video, we are going to look at variadic templates.

00:05.100 --> 00:12.240
A variadic function is one which can take any number of arguments. If you have done any C programming, or

00:12.240 --> 00:17.660
you were taught C++ by someone who thinks it is really C with a few extra features, then you will have

00:17.670 --> 00:21.000
probably used the printf() function for doing output.

00:21.990 --> 00:29.190
This takes a const char pointer as its first argument. Which is a string, a C string, containing formatting

00:29.190 --> 00:35.670
information. And then it has a variable number of arguments, containing the data to be displayed.

00:37.050 --> 00:43.500
This is not type-safe, so you can do things like saying you are going to print a C-style string, and then

00:43.500 --> 00:45.780
giving it an integer, which is not a valid pointer.

00:46.230 --> 00:47.430
So your program will crash.

00:47.940 --> 00:51.180
You can also provide the wrong number of arguments, and there is no check on that.

00:51.690 --> 00:57.450
And that is because all this argument processing is done at runtime. And it only works with types

00:57.450 --> 00:58.440
in the C language.

00:58.740 --> 01:00.330
It does not understand objects,

01:00.330 --> 01:06.000
for example. In C++11, we have variadic template functions.

01:07.560 --> 01:09.800
These are variadic functions which are done

01:09.810 --> 01:13.620
at compile time, so they are very efficient. And they are completely type safe.

01:15.150 --> 01:20.100
The syntax for this looks rather complicated, when you see it for the first time.

01:20.880 --> 01:27.690
If you think of a normal template function, we would have the template premises listed explicitly.

01:28.080 --> 01:32.190
So we would have "typename T", comma, "typename U", and so on.

01:33.150 --> 01:39.990
Then we would have the function arguments. So we would have "T t1", "U u1", comma, and so on.

01:41.250 --> 01:47.340
With a variadic function, we can have any number of template parameters and any number of function

01:47.340 --> 01:49.620
arguments, so we cannot do that.

01:50.250 --> 01:58.500
So instead there's this syntax. "typename", with three dots after it, represents a list of template

01:58.500 --> 01:59.430
parameter types.

02:00.390 --> 02:05.190
And then this list, followed by three dots, represents a list of arguments.

02:05.610 --> 02:09.540
And the types of these arguments will match the template parameters.

02:11.040 --> 02:14.520
These lists with three dots are known as "parameter packs".

02:14.790 --> 02:21.900
So we have one parameter pack which represents the template parameters, and one parameter pack which

02:21.900 --> 02:23.670
represents the function arguments.

02:26.910 --> 02:27.990
So how does this work?

02:28.590 --> 02:36.300
When we use a function template, which is not variadic, the compiler will deduce the template parameters,

02:36.660 --> 02:41.400
and then deduce the argument parameters from those.

02:42.510 --> 02:44.190
When we have a variadic function,

02:44.340 --> 02:47.250
the compiler needs to deduce the number of arguments as well.

02:48.870 --> 02:54.240
So if we have this function and we call it with one argument, which is a string. Then the compiler

02:54.240 --> 02:59.400
will realize it needs one template parameter and it needs one argument.

03:00.420 --> 03:04.070
And then this will be instantiated as a function which takes string.

03:05.400 --> 03:10.470
If we call it with three arguments with different types, the compiler will realize that it needs

03:10.530 --> 03:13.140
three different template parameters.

03:14.130 --> 03:20.220
So the template parameter pack will be populated with three different types, int, double and string.

03:20.940 --> 03:26.370
And then the argument pack will be populated with 42, 0 and "text" (string object)

03:27.300 --> 03:31.650
And then this template will be instantiated as function taking int, double and string.

03:35.830 --> 03:38.950
These parameter packs are only available at compile time.

03:39.250 --> 03:41.050
We cannot use them when the program is running.

03:41.710 --> 03:44.590
In fact, there are only three things we can do with a parameter pack.

03:45.130 --> 03:52.710
We can use sizeof, with three dots, and that will give us the number of elements in a pack. We can use

03:52.720 --> 03:58.900
make_tuple() to store the arguments in a tuple object. Which is very useful, because that makes the arguments

03:58.900 --> 04:00.370
available at run-time.

04:01.150 --> 04:06.510
And we can also iterate over the elements, using template recursion. Which we have not covered yet, but

04:06.510 --> 04:07.960
I will cover that later on.

04:10.300 --> 04:13.360
So first of all, let's try out the sizeof operator.

04:13.720 --> 04:19.450
So we're going to call this variadic template function with one argument and three arguments.

04:20.110 --> 04:25.330
Then we are going to print out the result of calling sizeof. And we expect to get 1 for this call

04:25.330 --> 04:26.530
and 3 for this call.

04:28.400 --> 04:28.910
And there we are.

04:29.280 --> 04:36.020
And do not forget to put the dots after the "sizeof"! This will cause the parameter pack to be expanded. Which

04:36.020 --> 04:41.150
means this pack will be replaced by a list of the function arguments, separated by commas.

04:44.630 --> 04:53.420
And then with the tuple. Same function call in main(). We call make_tuple(). We pass the argument pack and

04:53.420 --> 04:58.310
this time we need to put the dots after the argument pack, to expand the argument pack.

04:58.640 --> 05:05.780
So again, this will be replaced by the arguments, separated by commas. And then we can do

05:05.800 --> 05:07.520
"tuple-related" things. So we can call get().

05:08.360 --> 05:11.810
We do not know how many arguments there are, but we can assume there will be at least one.

05:12.200 --> 05:13.760
So it is safe to get the first element.

05:16.050 --> 05:17.940
And the first argument is 42.

05:20.460 --> 05:20.750
Right.

05:20.790 --> 05:21.360
Template

05:21.360 --> 05:26.700
recursion. You have seen, I hope, how you can have recursion with non-template functions.

05:26.970 --> 05:31.530
You have a function which calls itself. And normally, you make some kind of change to the arguments,

05:32.070 --> 05:37.260
As with the famous factorial example, where you decrease the argument by one each time you call it.

05:38.010 --> 05:45.120
And then you have a version, which is when the argument gets down to 1, and you stop recursing. You can

05:45.120 --> 05:46.110
do this with templates.

05:46.410 --> 05:52.740
So we could, for example, have a function which takes three arguments. And then this calls another

05:52.740 --> 05:57.390
version of the function, which takes two arguments. And then that calls another version of the function,

05:57.390 --> 06:00.540
which takes one arguments, and then we have no more arguments.

06:00.600 --> 06:01.830
So the recursion stops.

06:03.000 --> 06:09.000
So this function, could do something with the argument "t", and then call the same function with the

06:09.000 --> 06:09.840
remaining arguments.

06:10.680 --> 06:15.900
And this will do something with the argument "u", and then do something, and then call itself again with

06:15.900 --> 06:20.040
the remaining argument and then this can do something with the argument.

06:20.040 --> 06:27.450
"v". So if you imagine this as a list of three elements, then this function will pop the first element

06:27.450 --> 06:34.620
off the list. And then it will call itself again, pop the second elements of the list. And then pop the

06:34.620 --> 06:35.790
last element of the list.

06:39.210 --> 06:41.070
So that is if we know we have three arguments.

06:41.460 --> 06:45.510
If we don't know how many arguments there are, then we need a variadic template.

06:46.050 --> 06:47.880
And actually, this works slightly differently.

06:48.300 --> 06:55.830
We have one parameter, one argument, which is a single template parameter, a single argument. And then

06:55.830 --> 07:00.000
the rest of the parameters and the arguments are represented by parameter packs.

07:00.840 --> 07:07.110
So if we call this with three arguments, then "t" will be the first argument, and the pack will contain

07:07.110 --> 07:08.430
the second and third arguments.

07:09.300 --> 07:11.340
Then we do something with the first argument.

07:12.780 --> 07:13.670
Then we call it again.

07:13.700 --> 07:18.780
We pass the second and third elements. So, the members of the parameter pack.

07:19.980 --> 07:20.880
Then it gets called again.

07:20.910 --> 07:26.190
The second element will be this argument T, and the parameter stack will contain the third element.

07:29.320 --> 07:35.710
And then we need to have a function with a single argument, which will finish the recursion by processing

07:35.710 --> 07:40.000
the last argument. And, one important detail.

07:40.180 --> 07:45.340
This non-variadic function must be declared before the variadic template.

07:47.920 --> 07:50.650
So here we are, with our variadic template function.

07:51.520 --> 07:54.310
We are going to do something with the single argument.

07:55.360 --> 07:59.410
So this will take a single argument, and then the rest of the arguments in the parameter pack.

07:59.950 --> 08:05.770
So this will pop the first argument off the list, then call itself with the rest of the arguments.

08:06.460 --> 08:08.560
Then the second element will now become this argument

08:08.560 --> 08:14.470
"t". And this pack will contain the remaining elements, from the third element onwards. Then we process

08:14.470 --> 08:16.300
the second element, and so on.

08:16.930 --> 08:24.010
And we have our non-variadic function declared above the variadic one, and this will process the last element.

08:24.820 --> 08:26.110
So that will be the last argument to

08:26.110 --> 08:26.860
the function call.

08:30.130 --> 08:33.250
And then, in main(), we call this with int, double and string arguments.

08:33.730 --> 08:39.140
So the first time through, "T" will be an int. "Args" will be a pack containing double and string.

08:40.270 --> 08:41.710
And this parameter here will be "i".

08:43.780 --> 08:48.400
Then this argument parameter pack will contain "d" and "s".

08:49.810 --> 08:54.730
Then this will process the first argument and call itself again. The next time,

08:54.910 --> 09:02.560
The first argument will be "d", the parameter type will be double, and the pack will be just the

09:02.560 --> 09:03.400
string argument.

09:04.300 --> 09:06.910
And then, finally, this function is called with the string arguments.

09:09.490 --> 09:13.110
So there we are. The first time through, we have a call with three arguments.

09:13.120 --> 09:15.310
It processes the int argument,

09:15.310 --> 09:17.470
42. The next time

09:17.470 --> 09:24.280
through, we have two arguments. It processes the double. And then, finally, it calls the non-variadic template with

09:24.280 --> 09:26.320
the argument "text". And then finishes.

09:27.790 --> 09:29.500
Okay, so that is it for this video.

09:30.160 --> 09:30.970
I will see you next time.

09:30.970 --> 09:33.220
But until then, keep coding!
