Templates, the STL, and Other Skills
The course is part of this learning path
In this course, we will build on your existing foundational and object-oriented oriented skills and enhance them by looking at templates, the Standard Template Library, and other skills to help you in your builds.
- Learn about function templates and class templates
- Learn how to write efficient and excellent code with the data structures and algorithms in the standard template library
- Learn about smart pointers to manage dynamic memory automatically
- Understand friend functions, friend classes, and operator overloading
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In this lecture, we will be covering a powerful tool you can add to your C++ Toolbox. We will be discussing templates which generally come in two forms: function templates and class templates. Their purpose is to reduce duplication of code, and in turn increase reusability of code. In many programming languages, templates are called generics because they enable generic implementation of functions and classes. That will be automatically specialized by the compiler. Let's discuss function templates. For starters, function templates are generic functions that can work with any type of data or many types of data depending on the implementation. The developer writes a function template with placeholders instead of specific data types for parameters and often returns values of a parameter type as well. When the compiler encounters a specific type of data being used with a function call, it generates the specific code necessary to handle that particular type of data. Function template technology builds on the advantages we get from function overloading.
However, with function overloading, you still have to write multiple versions of the function which often do exactly the same thing. In other words, they have the same programming logic. It reduces the duplication of effort in such situations if a function template is used instead of writing function overloads explicitly. In software engineering, again, we say this increases the software reusability. Clearly, if the logic is different, function overloads are appropriate. But if we have the same operations and logic being performed, then it is useful to use function templates instead of function overloading. Let's take a look at some code. We'll start by creating a project called TemplateFun. 'Create new project', 'Empty Project' and TemplateFun. There we go. We will fill in the main skeleton. All right, so, main.cpp, include iostream, include string. Okay, there we go. And let's do a couple function overloads to start with. Just to get a feel for it. So, double a, double b. What we're going to do is, we're writing a function or a bunch of function overloads that will return the bigger of two elements that you pass to it.
So it's very simple. It's pretty much trivial but it's giving us an idea of what to expect. And with strings, we'll go with lexicographic ordering. So, it basically uses alphabetic or uses the lexicon and determines if the ASCII value or Unicode value is greater or less than depending on what types of characters are involved. Oops, here we go. And then the string version. All right, now let's look at what these are going to look like. It will return the bigger of the two. So, we just used a and b to keep it simple, but you know, Dub1 and Dub2 or mydouble1, mydouble2, whatever you want. But since these don't have a particular value beyond just being parameters, we just kept it real simple. it's doing simple math. So, a and b are okay. return a, return b. That looks kind of similar, doesn't it?
Actually, that's exactly the same. You know what? I'm going to save myself some time here. Copy and paste. And that right there should give you a hint that something might be wrong because I just copied and pasted code. Not necessarily wrong, just there might be a better way to do it.
Anyway, let's run it and see what happens. Well, actually, I guess we first have to do main(), right? Let's fill in main(). We have to fill in main() so we actually use it or we're going to have bigger problems. Okay, so the biggerDub = getBigger( 3.14, 5.55). Just pass it different values here. int biggerInt = getBigger(11, 9) and string biggerString = getBigger(John, Alice). And let me think here. I think, to make it a little cleaner, we're going to do name1 = John and string name2 = Alice. Now, there's a reason I'm doing this. If you write string literals, when it does comparisons with string literals, the problem here is that it might consider them or it will consider them a what's called a c string, which is really just a pointer to a bunch of characters. However, if I explicitly say this is a string I'm passing in, well, this will actually work fine because it has a string up here. But, when we do the function overload, we're going to have a problem. It will create an overload based on character string, c string instead of an actual string object. So we're going to use name1 and name2 for these.
But in this particular circumstance, it wouldn't have any different behavior. But, the behavior could be very, very strange and not what you expect if we do the template version. Okay. biggerDub. And now, I'm actually going to do a little bit of copy-pasting. This isn't necessarily bad because I'm going to edit it. It's just to save me a little bit more work. biggerStr. All right. Looks pretty good to me. So, let's run it. And you could obviously change bigger in any of these to, say, greater if you don't like bigger. So, it says that the bigger items are 5.55, which Is bigger than 3.14, which is exactly what we want. 11 Is bigger than 9. And then John since it has a later ASCII value than Alice. Because the J comes later in the alphabet, it actually has a greater ASCII value. That means that the J will come after. So, J Is greater than A. So, it goes character by character if there's any match. Like if we have Mark and Mary, Mary would actually be considered greater than Mark because the Y at the end, even though they both start with M-A-R, the Y at the end has later lexicographic ordering than the K in Mark. So, just to give you a hint in case you weren't familiar with that.
So, 5.55, 11, and John. So, we should get the same result if we use function templates. But you'll notice that we have the issue that all of these have essentially the same implementation. Right? So, it's literally copy-paste for each of them. So, instead of using function overloads, let's use templates. So we're going to use a function template instead. It returns the type, whatever this type T is, it's just a placeholder. And you just put template class T. You'll sometimes see type here or something else, but usually we want to use template class T. All right, so we'll copy this right here. And we can get rid of all these definitions because we're not using them anymore. So, we're just going to put this down here and we're going to say if (a > b), return a. And otherwise, we don't really even need an else, just like before, we just do that. And what will happen is, when the compiler encounters each of these calls, it'll say, that one has doubles and that one has integers and that one has strings, right? And it will generate the actual code needed specifically for each of those.
So, let's run it and see if we still get the desired output. Here we go. 5.55, 11 and John. So, a lot less writing, it's a lot more compact, it keeps the code cleaner. So, this is better. But like I said, if we put John and Alice here in double quotes, it would actually be a little bit strange because it would generate a version, not that takes strings, but that takes character strings or c strings. So, I think it was a pointer to an array of characters essentially, is what I would think it was and that would not necessarily give us the answer we're looking for, the solution we're looking for. So, that would be a very, very strange logic bug. Now, so we had to write much less code and get the same result. The compiler looks at the different calls again to get bigger that have different data type parameters and then generates the functions for each of those calls. This saves us a lot of time and effort. Obviously, this template approach to functions would work for any type for which the greater than operator is defined.
And since C++ allows us to overload operators, more on that later, we can define the greater than operator, just like we could any function. So, this function template is incredibly useful. The other type of template, there's the second type, is class templates. This allows an entire class to handle different types. So, what we're going do is we're going to add a class named swapper to our TemplateFun project and then test it out in main. There's one significant difference though, that we need to consider when dealing with class templates compared to most classes. We have technically tried to divide the implementation and specification into separate files, the dot H, the dot CPP. However, with templates and the current way that compilers work for C++, we actually can't separate. So, we cannot use separate compilation with templates. So, you can either put the full implementation inside the class declaration itself or you have the option of creating the class declaration and then using the same syntax with scope resolution, just like you would in the separate implementation file but with everything in the same file, the dot H file.
Some people will actually change this to a dot HPP file because the extension doesn't really matter. But dot HPP, some people look at that as a special full-implementation file for C++, but it's really up to you. We'll create a class that does something super simple. It takes two parameters of the same type or generic type, and allows you to swap them with a swap method. Then, you can always call, get first and get second to see what the official quote first and quote second values are. So, we just have to create a single swapper dot H file for our class. So, let's create it. So, we'll do header files, add new items and then we'll make the swapper dot H. We want to of course, include or I'm sorry rather, ifndef, let's start with that first. Swapper H need the include guards, right?
Define SWAPPER_H and then endif, class swapper. Now, above class swapper, we need to put the template prefix, so template class T. And then inside of here, we're going to put public, just like we normally would. We're making a class template and then the compiler would generate the appropriate types depending on how we use this. So, we have first and then T, the templated type second. We're going to have a swap function that can't be constant because it actually changes the internal data we're going to hold. We're going to have, get first and get second, not get last. We could make get last I suppose, but, all right. And, the private data is just the first value of type T and the second value of type T. Now, T is just a placeholder for right now. Now, where did the implementations go if we can only put them in one file? So, you could actually remove the semicolon, put curly braces and fill it in right here. I think what we're going to do, we're just going to grab these and we're just going to go below the class here, and I'm going to do this and we're going to use the same syntax that we would, if we were using a separate compilation technique. All right. And of course, we have swapper and the scope resolution operator, and we also need again, template class T above every single one of these that uses it, and that would be, I think all of them. So, since all of them are using it, we need to put this above each of them. So template class T. And, let's see here what we've got inside here. I'm going to say T temp equals first, first equals the second, and then second equals the temp. You can't say first equal, second, second equals first because by that point, first will have been overwritten by second. So, we have to grab that, initially. What is this, nope? That is, that should be right. Let's see if it, maybe it'll clear it up once we start compiling.
Now, let's grab this guy right here and put it right there, and then there, and then there. All right. Looks pretty good to me. I know, I know what we're missing, that's why it's red. Okay, I forgot about this. One other thing you have to add. So, there's a little bit of a difference in this syntax. You have to put an angle braces next to swapper. You have to say, it's a swapper that is swapping type T. So, you have to put the angle braces here. So, that's really the only difference. So, I'm going to just going to copy that, T right there. Sorry about that guys and gals. Here you go, type T. Perfect. Now everything, all the errors went away. I thought it was just the compiler being stubborn but then it dawned on me, we were missing something. See, everyone messes up and everyone does goofy things sometimes. No big deal. All right, so, oops, sorry, that is this is really bad. That is what swapper or swap is supposed to do, not what the constructor is supposed to do. So, I really goofed this time. Alright, cut that there, paste it there. So, this is what swap is supposed to do. Create a temp and send it to first, set first to second, and set second to temp, so that we officially swapped the order, and if you keep calling it it'll keep swapping them back and forth.
Now for the constructor, this is pretty easy. We set first equal to whatever the first is, that they pass on and then second equal second. Alright. So, that's pretty good for both of those. Now get first is very simple. Return the first and of course, forget second, return second. All right, that's pretty cool. Now, inside of main, I need to go up here. We need to include swapper.h at the very top in main. I'm going to, let's see what we want to do here, do I want to remove some of the other stuff? I don't think I will. I think I'm going to just add on. So, let's do this. Let's make a swapper of vintagers called intSwapper, and it's going to initiate or be initialized with 5 and 10 rather. And then for string we'll call it string swapper that will contain John and Bob as its two values. And then, what are we going to do? So, what are we going to do? We are going to put a little bit more space between bigger items down here and what we're going to do below it, maybe two end l's right there. All right, good. And now let's say, testing the swapper. Okay, pretty good. Now, cout dot intSwapper.getfirst. And I'm going to put the intSwapper.getsecond. And then, we're going to put cout, stringSwapper .get first, followed by a space and then followed by stringSwapper .getsecond. Excellent. Maybe couple end l here as well, and then we're going to call intSwapper.swap and stringSwapper.swap. Pretty good. So, we've swapped them and I think what we're going to do is, we're just going to, I'm just going to copy this and in this case I could break it out in a function, but this is, you know, basic repetition of testing. So, not too big of a deal. We're going to say after the swap. So, after the swap, what data do we have? Right? Okay. So, I think we've got everything we need. Let's try to run this, see if we got any weird errors or anything or if everything works. We'll still get the output from the bigger items. And then we have testing the swappers.
So, the initial data in the swapper was five and 10 in the int swapper, that we declared up here, that was a 5 and 10, and then we had John and Bob which were the strings for the string swapper. We called swap after printing out these things we called swap on each of them and you'll notice that the 5 and the 10 are reversed and also that John and Bob are reversed. Nice. So, it looks like this works great even for multiple types. So, we know how to work with both function templates and class templates now. These are great skills to add to our current abilities and knowledge. Before moving on, I have a challenge for you. I want you to continue with the template fun project and create a function template called get smaller. It should do essentially the opposite of get bigger. That is, it should return the smaller of two parameters that are passed in. Don't forget that before both the prototype and the definition of any function template you write, you need the template prefix, that is, the syntax with template class T. Otherwise, you will get a compiler error. Also, don't forget to test your new function template on say, multiple types like we did with get bigger. So, pause the video and come back when you're done or if you need some help.
How did that go for you? Were you able to write the function template? Let's do it together. Alright. So, shouldn't be too hard here, template class T, and we have T getSmaller, T a, T b. I'm going to grab this and of course, we're going to paste it down near the bottom and work on this. All right. Here we go. Paste that. And if, a is less than b, we're going to return a. Otherwise, we will return b. Awesome. Now, we probably want to test this now. So, how are we going to do that? Well, under the bigger values over here. I'm going to make double smaller dub equals get smaller of 3.14 and 5.55. We're going to have int smaller int equals the gets smaller of 11 and 9. And string smaller str equals get smaller of the strings, name one, name two. All right. And of course, we want to print all this stuff out. So, under bigger items, I could, I suppose copy and paste and then just be very careful, and we'll say smaller items and I'm going to say smaller dub for double, smaller int, and then smaller string. All right, looks good to me.
Let's try to run this and see what happens. Debug, start without debugging. Alright, not bad. So, you can see testing the swapper, got it done the smaller items before that. So, the smaller items are what we just tested and we should get the opposite of what was here. And we do, 3.14, 9, and Alice are the smaller items. So, great. You just wrote your first function template on your own. So, we're able to work with integers, doubles and even strings. And we could code more if we had to. Good job everyone, In the next lecture, we will begin discussing a C++ library that makes use of templates called the Standard template library or STL. I'll see you there.
John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.