WEBVTT

00:00.330 --> 00:06.090
Hello again! In this video, we are going to have an overview of templates. So we are going to find out

00:06.090 --> 00:10.020
what templates are, what they look like and how to use them.

00:12.830 --> 00:15.620
So first of all, why are templates useful?

00:16.010 --> 00:20.060
What problem are they meant to solve, or at least help us with?

00:21.800 --> 00:28.580
We often run into situations where we have several bits of code, which are basically the same, but

00:28.580 --> 00:30.290
work with different types of data.

00:30.830 --> 00:34.010
So we have a vector of int, vector of double, vector of string and so on.

00:35.900 --> 00:42.500
If we implement these the obvious way, then there would be three classes. One which uses int, one

00:42.500 --> 00:44.300
which uses double and one which uses string.

00:44.570 --> 00:47.750
But all the actual vector functionality would be exactly the same.

00:48.740 --> 00:54.080
And then later on, if we write a class of our own and we decide that we'd like to have a vector of

00:54.080 --> 00:59.090
this class, then we need to implement another the class for vector of "my type".

01:00.530 --> 01:06.140
So that's obviously not a very good way of doing things, and because C++ is strongly typed.

01:06.590 --> 01:14.840
We can't just create a vector of "anything" or a vector where the type is checked at run time, because

01:14.840 --> 01:19.370
C++ does not work like that. What we do have is templates.

01:21.060 --> 01:24.030
And templates allow us to write code that works with any type of data.

01:24.390 --> 01:29.910
So basically, we can write the functionality for the vector and then we can plug in the actual type

01:30.480 --> 01:31.650
when we use it.

01:32.790 --> 01:34.590
And this is known as generic programming.

01:34.740 --> 01:40.080
I do not mean that templates are generic programming, but it is an example of generic programming where

01:40.080 --> 01:41.100
you can use any type.

01:45.560 --> 01:51.440
And then when we actually use this template, we say what type of data we need.

01:51.710 --> 01:55.730
And then the compiler will go through and generate the actual code.

01:56.060 --> 02:02.300
So if, for example, we want a vector of int, the compiler will go to the definition of the

02:02.300 --> 02:02.990
vector class.

02:03.800 --> 02:10.130
It will plug in the int as the data type and then it will generate a class, which is vector of int.

02:10.580 --> 02:12.170
And this is actually its own type.

02:13.670 --> 02:20.330
So this is quite different from generics in Java, where there's only one vector class and the generics

02:20.330 --> 02:23.150
are really there to check that you're using the types correctly.

02:23.660 --> 02:28.670
In C++, you get a different type for every vector class.

02:32.220 --> 02:39.990
So the pilot will generate this code for the class of a vector of int, and then it will insert the source

02:39.990 --> 02:42.870
code for the class definition into the translation unit.

02:43.350 --> 02:48.360
The translation unit is basically a source code file, as it is seen by the compiler.

02:49.050 --> 02:55.290
So that's the .cc or .cpp file, with all the headers included and pre-processed.

02:55.920 --> 03:02.010
So in effect, the compiler is going to paste this new bit of code into its interpretation of the

03:02.010 --> 03:07.290
source code, and then the compiler will go through the code again.

03:07.740 --> 03:09.780
Actually, compilers go through the code several times.

03:10.140 --> 03:11.850
So this is just another phase.

03:12.600 --> 03:15.930
And then when the compiler goes through and actually converts it all to assembly.

03:16.320 --> 03:20.100
Then this is going to be included in the compilation as part of the program code.

03:24.870 --> 03:31.620
So this is known as a "template instance", the vector of int. And the process in which the compiler generates

03:31.620 --> 03:35.550
the code for a vector of int is called an "instantiation".

03:39.540 --> 03:45.420
This is all done automatically. When the compiler sees vector of int, it will go off and generate this code.

03:46.650 --> 03:52.890
Obviously, the compiler must be able to see the full definition of the class or the function body.

03:53.340 --> 03:54.990
It must be in the same translation unit.

03:55.470 --> 03:57.390
And normally that means putting it in one of the headers.

04:04.570 --> 04:08.290
When we write a template, we use a dummy.

04:08.650 --> 04:14.500
So this is to show the compiler what the code will look like. And the technical name for this dummy is

04:14.500 --> 04:15.970
the "template parameter".

04:16.420 --> 04:19.870
So that is like the int in the angle brackets in the vector.

04:23.400 --> 04:26.240
We can have templates which are functions or classes.

04:26.660 --> 04:30.110
So let's look at a function, as it is a bit simpler to write.

04:31.840 --> 04:37.300
We start off by putting the template keyword, so that tells the compiler we are about to declare a

04:37.300 --> 04:37.900
template.

04:38.410 --> 04:46.090
Then in angle brackets, we put the template parameters, we put class and then the name that we are going

04:46.090 --> 04:47.260
to use for this parameter.

04:47.650 --> 04:52.030
The convention is to use T, although you can use anything you like.

04:53.200 --> 04:58.210
Another convention is that the parameter starts with a capital letter. So that makes it stand out from

04:58.210 --> 05:02.350
the rest of the code because normally in C++, we use lower case.

05:05.610 --> 05:11.130
And then we write out the function, except we do not put any concrete types, we just put the template

05:11.280 --> 05:11.460
parameter.

05:12.630 --> 05:18.120
So this is going to be a function of Max, which takes two of these parameter types by reference to

05:18.120 --> 05:20.850
const and returns another one by value.

05:21.780 --> 05:24.000
And then we write the actual function body.

05:24.570 --> 05:26.070
So that is just like a normal function.

05:26.430 --> 05:30.090
If one argument is greater than the other, return the first one.

05:30.570 --> 05:31.920
Otherwise, return the second one.

05:35.730 --> 05:42.780
And then when we call it this function, the compiler will replace this T by the type that is needed

05:42.870 --> 05:44.400
to make the function call work.

05:50.130 --> 05:57.390
So, for example, if we call Max with the arguments 7.1 and 2.6, the compiler will deduce that it

05:57.390 --> 06:01.050
needs to use double, so T will be replaced by double.

06:03.080 --> 06:07.310
And then the compiler will generate that code and add it to the translation unit.

06:07.940 --> 06:09.980
So it is going to write something like this.

06:13.280 --> 06:17.960
Obviously, the compiler does not actually put comments in the source code! But we end up with a function

06:17.960 --> 06:20.540
that takes two doubles and returns another double.

06:23.980 --> 06:25.540
So here is that code.

06:26.280 --> 06:33.490
Here is the definition of the template function. Here is the template with the parameter T.

06:35.620 --> 06:40.840
And then we call the function in main. We can, if we wish, put...

06:44.260 --> 06:48.100
in there, and that will force the compiler to instantiate it with a double.

06:49.330 --> 06:52.930
Or we can just leave that out and let the compiler work it out for itself.

06:54.190 --> 06:58.610
And then the compiler is going to go in here and replace it by double.

06:58.990 --> 07:00.970
So it'll end up with code that looks like this.

07:05.650 --> 07:07.360
And then there we are, seven.

07:07.690 --> 07:11.560
So seven is greater than 2.6, which is nice to know.

07:20.080 --> 07:25.840
The way that you organize your code is quite important when you are using templates. If you are just

07:25.840 --> 07:27.460
using a normal function,

07:27.760 --> 07:29.920
you just need to give the declaration.

07:30.460 --> 07:35.800
So the compiler will need to know the name of the function, the number and types of its arguments,

07:35.800 --> 07:39.250
and also the return type. And the compiler is just going to check

07:39.670 --> 07:46.210
that the types are used correctly when the function is called. When we call a template function,

07:46.570 --> 07:50.920
the compiler must be able to see the full definition, including the function body.

07:53.830 --> 07:59.230
And the reason for that is that the compiler is going to go through the function body and replace the

07:59.470 --> 08:04.150
template parameter or parameters - you can have more than one - with the necessary types.

08:07.570 --> 08:12.910
The easiest way to do this is to write the template definition in the header, as an inline function,

08:13.420 --> 08:16.150
so it automatically gets included and the compiler can see it.

08:17.230 --> 08:24.580
Some programmers prefer to write the templates in a separate file and include that as a separate include file.

08:30.830 --> 08:33.230
So here is a class template.

08:33.650 --> 08:40.250
So again, we have the template keyword, we have the parameters in angle brackets, and then we just

08:40.250 --> 08:46.040
write the class definition using the type parameter where the type of the data would go.

08:47.030 --> 08:52.220
So this is a class that is going to have a member of this type, and its constructor is going to take

08:53.210 --> 08:56.150
an argument of this type and initialize the member from it.

08:57.810 --> 09:02.700
And then to create an instance of this class, we put the type in angle brackets.

09:05.990 --> 09:12.560
We have to do that, at least until C++ 17. With the function, it is optional, with a class, it is compulsory.

09:12.890 --> 09:17.990
So this is going to instantiate the Test class using string as the parameter.

09:18.890 --> 09:21.500
And then there is our constructor call with the argument,

09:21.680 --> 09:27.770
"Hello", and then the compiler is going to go through and generate some class like this.

09:29.270 --> 09:34.880
So it is going to have some strange name, which the compiler knows means a Test with the string parameter

09:35.660 --> 09:39.350
and then the parameter T will be replaced by string.

09:39.360 --> 09:43.550
So the data member is a string and the constructor takes a string argument.

09:52.680 --> 09:55.740
I was saying that before C++ 17, you have to give the type.

09:56.190 --> 09:58.980
And now in C++ 17, that is actually optional.

09:59.370 --> 10:02.190
The compiler can actually deduce the type.

10:02.970 --> 10:07.200
So there was some difficulty, apparently with deducing the type of a constructor call.

10:08.430 --> 10:11.310
So we've had to write vector angle brackets int.

10:12.710 --> 10:18.230
And now, if we have an initializer, the compiler can deduce the type from the initializer.

10:18.590 --> 10:24.380
So if you put vector vec with initial values one two three, then the component will realize this is

10:24.380 --> 10:24.910
a vector of int.

10:25.700 --> 10:30.590
And it will instantiate a vector template with the parameter as int.

10:33.530 --> 10:39.710
And this is known as CTAD, another C++ acronym, which is actually almost meaningful, which is

10:39.710 --> 10:41.240
quite good for a C++ acronym.

10:42.260 --> 10:45.260
So this stands for Constructor Template Argument Deduction.

10:46.430 --> 10:50.970
So it means we can call constructors the same way we call a templated function.

10:50.990 --> 10:55.280
We do not need to give the type of the parameter.

10:56.360 --> 11:01.010
It only works if the declaration has an initializer in it, otherwise the compiler cannot work out

11:01.010 --> 11:01.730
what the type is.

11:02.390 --> 11:03.950
And then the compiler will deduce it.

11:04.550 --> 11:07.250
And this does make many declarations simpler.

11:08.570 --> 11:10.880
So here is a very simple example of that.

11:11.150 --> 11:14.600
So this is how we had to write it out before C++ 17.

11:17.200 --> 11:21.150
We have to say that the type parameter is int. Now with C++ 17,

11:21.190 --> 11:23.830
we can let the compiler do the work for us.

11:24.310 --> 11:27.880
And this requires a C++ 17 compiler, so let's do that.

11:28.420 --> 11:30.100
So project properties,

11:31.540 --> 11:32.410
language standard,

11:34.620 --> 11:35.910
C++ 17.

11:36.900 --> 11:37.770
So let's see if this one.

11:37.920 --> 11:38.580
I think it should.

12:09.680 --> 12:16.080
Oh, I see so you can declare it without a type parameter, but you cannot use a range for loop with

12:16.080 --> 12:16.290
it.

12:24.250 --> 12:26.140
MinGW will help us.

12:41.720 --> 12:44.120
So that is the code I am going to be compiling.

12:47.470 --> 12:48.250
Yep, no problem.

12:51.930 --> 12:54.270
So there we are, vec equals one, two three.

12:57.390 --> 13:04.590
And finally, the typename. I have put class in here, because that is the way it has been done

13:06.300 --> 13:08.070
originally, since templates were added.

13:08.490 --> 13:15.540
(I think that was the ARM) and in C++ 98, there is also a typename keyword, which you can use instead.

13:16.140 --> 13:17.280
So you can use either of these.

13:18.540 --> 13:23.550
The reasoning is that "class" is a bit confusing because you do not necessarily have to have a class as the

13:23.550 --> 13:24.120
template.

13:24.930 --> 13:26.160
So we have typename instead.

13:26.490 --> 13:30.370
But many programmers still continue to use it.

13:30.420 --> 13:36.270
I think I have mostly managed to retrain my fingers by now, but there might be the odd one that slips through!

13:38.380 --> 13:40.450
OK, so that's it for this video.

13:40.870 --> 13:43.750
I'll see you next time, but meanwhile, keep coding!
