 Hi, welcome to this course on beginning C++ templates. I'm Umar, your host. Let me give you some background about myself. I have a degree in civil engineering, but I've always been passionate about programming. This course is an excerpt from another course called Beginning Modern C++. I will give you a quick overview of what I'm going to teach in this course. I'll introduce templates and then we'll move on to argument deduction, template instantiation, then we'll also look at the specialization of function and class templates. I will also explain some common C++ 11 features such as veridic templates and alias templates. After this training is over, you'll be able to understand and interpret the complex syntax of templates and you'll be able to write your own function and class templates. Thanks for checking out my course. Hi, and welcome back. In this video, we'll learn about templates. Templates are used for generic programming. That means you can write algorithms and classes without regards to the data type. Let's understand it with an example. As you may know program, we want to find the maximum value from two numbers. So for this, we'll create a function. So the function name is Max, it accepts two integers and will return the one that has the largest value. So if x is greater than y, then return x, l's return y, and we can invoke this function from main. We may also print it. Let's run it. Perfect. If we also want to find the maximum value from two floats or two doubles, then we will have to write a function for each data type. Obviously, we can overload the max function for a specific data type. So we can just replace int with float. And it will also work for floating point numbers. And if we run this, it shows the correct value. If we want to use max to compare the larger value of some other types, then we will have to overload the max function for each type. And this is going to be tedious. It will lead to large number of functions. This is where templates can step in. Templates allow us to write a function that can operate on any kind of data type. Let's go ahead and convert this max function into a function template. Any kind of template always begins with the keyword template. Then you have to specify the placeholder for the types, and that is specified as type name, and then you have to specify some name. So we'll give the name as t. Now this t is the placeholder for the type, and it will be automatically substituted, by the compiler during compilation. In some books, you may see the keyword class being used instead of type name. And in this context, there is no difference between the users of class and type name. Both declare a name that is used as a placeholder for the type. The differences between the class and type name are apparent only when you do advanced template programming. But throughout our sessions, I will use the keyword type name instead of class. The return type will be t, and the arguments are also going to be of type t. Now we don't need the functions that we have implemented earlier. So let me go ahead and comment them out. Our program will still work because the compiler will automatically substitute this t with the type of its argument. So the argument types are floats, so t will be of type float. And that's not all, we can also invoke this function for other types. And if we run this, so it shows the correct output. So we did not have to write a function for each type. Instead, the compiler will automatically generate the function for the appropriate type. The compiler will examine the arguments of the function calls, and accordingly will deduce the type of t. So in this case, the type of t is deduced as float, and the second function call of max where we pass the integers, the type of t is deduced as int. That is why compiler will generate two copies of max function. One copy will be for float and other will be for the integer type. We can see the generated functions in the assembly code. So we'll go to the project properties, and go to cc++, then output files, and here we'll select assembler output. We'll select assembly with source code. Let's build the program again so that the files are generated, and then let's go to the project directory. We'll go to debug folder. The other debug folder that is below the project directory, the sub directory, and then we'll select source.asm. Let's open it, and let's search for the function max. And you can see that there are two copies of max function, max for float and max for integer. What would happen if we do not invoke max function at all? So if we just comment this code out, and then build again, and we'll see the assembler output. Try to search for the max function, and it only shows the max function that is in the comment, but there is no other max function. So this means that if the function template is not invoked, then the function is not generated by the compiler. The compiler will generate the function for only those types with which it is invoked. The process by which the function is generated is called as instantiation. The compiler will examine the arguments of the function template, and it will accordingly reduce the type of T. Once the type of T is deduced, the function is instantiated for that particular type. So using templates, we can create generalized software components. These components can be used in different situations and with different kinds of data. Use of templates leads to high performance algorithms and classes, because templates are used by the compiler to generate the code at compile time. Therefore, no runtime costs are involved. Many libraries have been implemented using templates, such as Active Template Library, Windows Template Library, Boost, Poco, Ace, et cetera. All of these are high performance C++ libraries used in various domains. As we saw in the earlier example, you can use templates to write functions. The function template will always begin with the template keyword. And in the Angular brackets, you have to specify the type names. A type name is a template type argument, and you can specify multiple such type names. Each type name is a placeholder for the actual type, and it can accept any type. The type name argument is substituted by the actual type at compilation time. The type name can also be used as the return type. The compiler uses a process called as argument deduction, and through that process, it deduces the type of the type name argument by examining the function arguments. Once it deduces the type, the function is instantiated for that particular type. Note that this process happens during compilation. We will understand this process in more detail in the next video. See you then. Hi, and welcome to the next part on templates. In this video, I'll explain the template argument deduction. This is a process by which the template type arguments are deduced. The compiler examines each function argument, and then from that argument, the corresponding type argument is deduced. This means if the argument type is integer, then the corresponding type argument is deduced as an integer. Once the type argument is deduced to be of specific type, then its subsequent deduction in other function arguments should lead to the same type. During template argument deduction, no conversions are performed. After the deduction process is over successfully, the template is instantiated. Sometimes we may need to overwrite the deduction process of the compiler, and we can do that by specifying the types in the template argument list as shown in the example. This is required in certain cases, and we'll see some examples later. A template function or a clause will only act as a blueprint. This blueprint is used by the compiler to generate the code, and the compiler generates the code after the template argument deduction. This process is known as template instantiation, and this process happens at compilation time. The template instantiation happens implicitly in the following cases. The first case obviously is when a function template is invoked. The second is, if you take address of a function template, then the compiler is forced to instantiate the function template. The third is, when you explicitly instantiate a function template. And finally, if you create an explicit specialization for a function template, then in that case also it is instantiated. We will learn about explicit instantiation and explicit specialization in the subsequent videos. For the template instantiation to work, the compiler should be able to see the full definition of the template. That is why function and clause templates are always defined in a header file. Templates generally do not use the conventional style of declaration and definition. The definition is implemented in the header file. Now let's look at some examples of the argument deduction and template instantiation. In this example, we'll just print the type of T using the type ID operator. And we will invoke max function with different data types. This is one and the second would be maybe through a double. Let's run it. In the first case, the type name T is deduced to be an int and in the second case, it's deduced to be a double. Now, as I mentioned earlier, once the type name is deduced, then in subsequent function arguments, it should be deduced as the same type. This means if for some reason, the second argument is of a different type, let's say it is a float, then this is a compiler error because when the compiler examines the arguments, from the first argument, the T is deduced to be an int. But from the second argument, it is deduced to be a float. The compiler will not apply any type conversions here. That is why this will lead to an error. Now, there are two ways of solving this problem. One way is you typecast one of the arguments to the other type. So maybe we can typecast three to be a float. And this would allow the code to compile. The second way would be to override the compiler's deduction process by explicitly specifying what should be the type of T. So for example, if in the second call, we specify the first argument as int and the second as double. So we want both arguments to be of type double. So we can override the compiler's deduction process by specifying the type of arguments here. Now the compiler will not use the argument deduction. It will directly replace this T with a double type. So these two function calls will cause the compiler to instantiate the max function for float and double type. A function template can also be instantiated when you take its address. So for example, if we want to take address of the max function, when it is invoked for integers, then we can do it like this. And this is the point of the function max. The argument types are integers. And this is how it will look like. Now we are not invoking the max function for integer types, but we got its address. That is where the compiler will instantiate the max function for integer types. And we can even verify this by first building it and then examining the assembly source. You can see that the compiler has instantiated max for integer types. You can also explicitly instantiate a function template. So if we want to instantiate max function for character types, we do it like this. Template max. This will cause the compiler to instantiate the max function for character types. Note that we are not invoking the max function for care types anywhere else. So we will build this and then we'll examine the assembly source and let's search for max of care. There you go. The max function for care types has been instantiated by the compiler. And finally, the instantiation also happens if you create an explicit specialization of the function template. But we will look at this feature in the next video. See you then. Hi, and welcome back to the next session on templates. In this video, I'll explain explicit specialization. Let's understand it with an example. In the previous videos, we implemented a function template called max. It accepts two arguments and returns the maximum value out of those two arguments. Let's use the max function for comparing strings. Maybe we have an array of strings and we want to sort those strings. So obviously we'll have to compare the strings in order to sort them. So we'll create two strings but we'll be using raw strings. So I have two pointers A and B and they internally point to the string A and B respectively. So we want to find out which string contains the maximum value. Obviously in string comparison, the ASCII values of the characters are compared. And let's see if our max function can do that. And we'll also print the result. Let's run and it has printed A. So obviously that is not right. It should have printed B. So why do you think it has printed A? To understand, let's debug the code. So we'll run up to this point. And if you examine the watch, you can see the values inside A and B variables and you can see they both hold addresses. And you can also see what those addresses internally contain. They contain the values A and B strings. And you can see that A has the address which is larger than the address in B. And the algorithm that we have implemented in max function will obviously compare these addresses. Therefore it will give the wrong result. So we need to implement the max algorithm in such a way that when we use it for strings, it should compare the values in the addresses rather than the addresses. So how do we do that? We can use the feature of explicit specialization. In explicit specialization, we specialize a function or a class template for a particular type. We do that because the algorithm that we have implemented may not suit a specific data type. Or it does not provide correct semantics for some specific data type. The other reason why we may want to explicitly specialize a function or class template is because the algorithm that we are implementing, we can implement it more optimally for a specific type. Therefore, we can write a new definition for that specific data type using explicit specialization. When you specialize a function template or a class template, then the definition should be written in a .cpp file and not in .h file. The reason is explicitly specialized functions are already instantiated. So if you define them in a header file, it will lead to violation of the one definition rule. The primary template definition should occur before you specialize function or class template. Now let's go back to the code and create an explicit specialization for the max function. So an explicit specialization is written like this. We will define the function max, but it will begin with the keyword template and the template argument list should be empty. Then we write the return type. The return type is going to be const char star. Then the name of the function and then you can specify the arguments. And here we'll write the algorithm that will compare the strings rather than the addresses. So we will use the C function string compare. So if x is greater than y, then string compare will return a value that is greater than 0. So we'll write it like this. If x is greater than y, then return x, otherwise return y. And we may also want to print a message here so that we know that the explicit specialized function is getting input. Now if we run this, we should see the correct output. First stop debugging and run it. Now it correctly shows b is greater. And you can see it has also printed the message max const char star. Note that an explicit specialization is already instantiated. That is why it should not be defined in a header file. It should be defined in a .cpp file. In explicit specialization, you may also specify the type for which you are specializing. You can write that type over here. Now this is optional. I would recommend doing this because this way it will be easy to differentiate between explicit specialization and explicit instantiation because both seem to have similar syntax. But note that in explicit instantiation there is no template argument list after the keyword template. But in explicit specialization after the template keyword you have to specify the empty template argument list. So I am just going to write a comment here. That's all for now. In the next video we will look at non-type template arguments. See you then. Hi and welcome back to the next part on templates. In this video I will explain non-type template arguments. Non-type template arguments appear in the template argument list. And these are constant expressions. Let me show this with an example. So we can create a function which will have a non-type template argument such as size and then we can use this size inside the function. This function does not accept any arguments. We will just print the value of size. To invoke this function we have to specify the value in the template argument list during invocation. So we can specify some value here. And if we run this we should see the value being printed. So you can see 3 is being printed in the console. But whatever value is specified here in the template argument list that should be a constant value. If we create a variable and try to specify that as an argument that is not going to work. If we try to build this this is an error. So the error is template parameter size i a variable with non-static storage duration cannot be used as a non-type argument. However, if we try to use size of the type because this is computed at compile time we can specify this as the argument in the template argument list of the print function. So the non-type template arguments should be constant expressions and it should be possible for the compiler to compute them at compile time. Let's remove this and change it back to 3. The non-type template arguments are always constant expressions. This means the variable size that we have here is constant and it cannot be modified. Let's try to modify it. If we build this the compiler will not allow us to build it. So size is constant. Because size is constant we can use it to specify the size of an array like this. And if we build this works. Non-type template arguments are more common with the classes. But with functions they are commonly used with arrays. Let's create a function that will return the sum of elements of an array and the array can be of any type. So we'll create this function called sum and this would accept an array. We'll accept it as a pointer and we'll also have to specify the size. And finally we'll return the sum. Let's create an array to use the sum function. We'll comment out this code. I forgot the semicolon here. Let's run it. And this is the output. To pass an array into a function you have to pass the address of the first element. And you also have to pass the size. Using templates and non-type template arguments you can pass an array into a function without specifying its size explicitly. So before I show you that let's create a reference to this array. A reference to the array should contain the size of the array. So the type is int and in brackets we'll use a pointer and here we'll use the size of the array and assign the array to this reference. Now ref becomes the reference of the array. Note that we have to specify the size of the array here. If we change this to 5 this will not work. So there is an error here because a reference is to an array of 5 elements but the array that we have on the right side only contains 4 elements. For the concept of reference to an array we can pass this array into a function without specifying the size and we will take the bondage of non-type template arguments. So I'm going to create a copy of this function. Let's comment out the old code. Now this size that I have specified here I'll put it over here as a non-type template argument and the argument to the sum function would be reference to an array. Earlier I mentioned the non-type template argument is going to be a constant. So it should be an expression or a value that is computed at compile time. So it can be used to specify the size of the reference to the array and to invoke this function we just need to pass the array variable and it shows the correct output. So using reference to an array and non-type template arguments we can pass an array into a function without explicitly specifying the size. This is used by the standard library to implement the global functions sd begin and sd end for arrays. So if you want to get the beginning iterator of this array we can use sd begin. And if we look into this definition of begin you can see it accepts a non-type template argument. And the argument of the begin function is reference to an array. A non-type template argument is an expression that is specified in the template argument list. This expression is computed at compile time. That is why it must be a constant expression. The constant expression could be an address, reference integrals, null pointer or enums. If the constant expression is an address then it should be a static address. That means it should have been computed at compile time. So you can specify a pointer to a variable or a pointer to a function. The non-type template argument is part of the template type. The non-type template arguments have been used in the standard library to implement the global begin and end functions which are used to compute the begin and end of static arrays. As I mentioned earlier non-type template arguments are more common in classes. So we will revisit non-type template arguments again with classes. That's all for now. I'll see you in the next video. Hi and welcome back to the next part on templates. In this part I'll explain veritic templates. Veritic templates are functions and classes that can accept arbitrary number of arguments. If you have already used C, you will know that printf function is a function that can accept any number of arguments. It is internally implemented through macros and because of that it has several disadvantages. Because of macros it is non-type safe. And secondly, such a function cannot accept references as arguments. Let's try creating such a function without using macros. We will use the initializer list feature of C++11 to implement this function. So we will create this as a function template so that the function can accept any type of arguments and we can use the range based for loop to access the individual elements and then we can print those elements. Now we can invoke this function like this and it works perfectly. The problem here is with initializer list the arguments have to be of the same type. That means if I want the second argument to be of a floating point type then the code will not compile because it will fail the template argument reduction. This is where variadic templates can step n. Using variadic templates we can write a function that can accept any type and any number of arguments. So let's write another version of the print function using variadic templates. I'll comment this out so that it doesn't interfere with our implementation. So obviously this is going to be a function template and we need to specify the type name. Now whenever you write a variadic template you need to specify that through the syntax of three dots. These three dots are called as ellipses and they are used in various places in C++. For example, we use ellipses to represent an all cache block in exception handling. Now here ellipses signifies a variadic template and we need to specify the name that will represent the variable number of type names. So we'll give a name here called as perums. This is known as the template parameter pack. Perums does not represent type name. Rather it is an alias to the list of type names. You can imagine perums to be a pack of template type names. The next thing is to define the print function with the arguments. Now we will use perums to signify the arguments of the function. This is known as the function parameter pack and it will be automatically expanded by the compiler. Let's write that here. Now this function can accept any number and any type of arguments. Let's invoke it with different type of arguments. The arguments that are being specified here will be automatically expanded by the compiler over here. Now the question is how do we access the individual arguments? The thing is you cannot access the individual arguments directly. Instead you'll have to rely on recursion. So we use recursion to access the individual arguments. That means in each recursive call the number of arguments is reduced by 1. And just like in normal recursion where we have a condition that ends the recursion here also we need something that will end the recursion. And that has to be done by writing another print function without using the template. And that function is called as the base case function and we will learn how to write that base case. So let's invoke this print function recursively to pass the function parameter pack. You have to use a syntax like this. Now the compiler will invoke the print function with the same arguments that were passed in the print function from main. If we compile this code it will compile fine but the compiler gives a warning. Obviously the code that we have written will cause a runtime stack overflow because of infinite recursion. As I mentioned earlier we need to write a base case that will stop the recursion. And we have to do that by passing one more argument to print function. So we will specify one more type name and this will be the first argument. Now when we compile this it gives an error. It says no matching overloaded function found. Expect two arguments, zero provided. So the thing is in each recursive call the number of arguments is reduced by one. And finally print will be invoked with zero number of arguments. Let me explain that how that works. So in the first case print is invoked with these arguments and the call will go into the print function and then the print function will be recursively invoked. Now obviously one will be passed as an argument here. And 2.53 and 4 will be passed as arguments here. They will be part of the function parameter pack. Then we pass the function parameter pack to print function again. Obviously the arguments that will be passed to the print function again would be 2.53 and 4. So the call will be 2.53 and 4. You can see that the number of parameters to print function has reduced by one. Now obviously when print is again invoked with these number of arguments 2.5 will become the first argument that will be captured in A variable and 3 and 4 will be passed as arguments here. So they will become part of this function parameter pack. So obviously the next time when print is invoked it will be invoked like this 3,4. Now when in this case print is invoked 3 will become part of the first argument A and the function parameter pack will only contain 4. That means when print is invoked again it will be invoked as 4. So you see in each recursive call the number of arguments is reduced by 1. Obviously when print of 4 is invoked 4 will become the first parameter and it will be captured in A and the function parameter pack will be empty. That means when print is invoked it will be invoked with 0 number of arguments. So the next call will be print and it will not accept any arguments. So during template instantiation the compiler will look for a function print that does not accept any arguments and obviously we don't have such a function but we can write a function called print that does not accept any arguments at all and that function is going to be our base case function that will stop the recursion. So let's go ahead and write this print function and now if we build this it does not give any error. Now we can access individual arguments and perform operations on them. We will understand this process again by looking at the call stack. So I'll keep a break point and print function and we'll start the debugging. So it has reached the base case we'll open the call stack here print is invoked with four arguments. So you can see the type names are int, double, int and char constar. And the next call is going to be the recursive call where the first argument is removed because that is consumed. So the first type name is double, then int then char constar. Then in the next recursive call it's going to be int and char constar and then subsequent call we only have char constar that is the last parameter that is four and finally after that is consumed as the first argument here it will invoke print without any arguments. And finally the recursion will stop. We'll stop here and we will continue this in the next video. See you then. Hi, welcome back. We will continue our discussion on veretic templates. In the last video I explained how we use recursion with veretic templates and how to stop that recursion using the base case function and we also saw how during recursion the number of arguments is reduced by one. Now we will print all the arguments that are passed into this print function and in the base case we will print end of line. Build this and run and you can see it's printing the arguments that we passed to print function but obviously they are printed together so we would want to have some kind of separator when they are printed. So we'll do one thing we'll just print a comma here. So that will be our separator but we can see an extra comma that is being printed after the last argument is printed. So how do we take care of that? We could print a comma only when we know the function parameter pack is not empty. So some way we need to find out what is the number of arguments in the function parameter pack and we can do that through the veretic size of operator. So we can use size of to find out what is the number of parameters in the function parameter pack and we can also use this size of operator to find out how many template parameters are in the template parameter pack and we'll comment this out let's run and you can see in the first call the number of parameters was 3 in the second it was 2 then 1 and finally 0. So we can take advantage of size of to check the number of parameters in the function parameter pack and accordingly decide whether to print a comma or not. So if size of args is not equal to 0 then we'll print a comma and obviously when the function parameter pack is empty the comma will not be printed that would mean the last argument is being printed perfect. If we pass user defined types to print function then obviously multiple copies will be created because we're passing by value. So let's pass the arguments by constant reference and here we'll specify that the parameters are reference types like this. Let's try this out with our integer class. So I'm going to add the integer class to the project I've added it to the project let's include the appropriate header file now we will invoke print function with our integer types and we'll comment out the first call so everything is fine it works. The problem here is that even though we are passing an r value here as an argument to print function when it is passed again during recursive call to print it will be passed as an l value and not as an r value. So we would like to use perfect forwarding here and obviously for that we'll specify the arguments as r value references. Remember the reference collapsing rules when we pass an l value to this function then this argument will become l value reference but if we pass an r value to this function as an argument then this will become an r value reference and we would like the same thing to happen to this function parameter pack. So this would be r value reference and we'll use std forward here note the syntax the ellipses comes outside the brackets of std forward now depending on whether l values or r values are passed to this function they will be appropriately forwarded to the next recursive call. That's it for now in the next video we'll start with clause templates. See you there Welcome back. In this video we'll start with clause templates. Clause templates are frequently used in C++ to create clauses that should be able to handle different types. Therefore it is more common to use clause templates with containers. C++ standard library already contains implementations of several containers that are implemented through templates such as vector list, set, map etc. To understand clause templates we will create a clause called as stack initially we will assume that it can only hold integers and we'll create a stack of 512 integers We'll initialize the top to minus 1 then we'll add functions such as push, pop top is empty and in pop we'll simply reduce the top value by 1 and if you want to access the element at the top of the stack we can use the top function we'll also write a function called isEmpty and it would be empty if top is equal to equal to minus 1. Let's create an instance of this stack alright and then in a while loop we can print all the elements that have been pushed into the stack. While the stack is not empty get the value from the top of the stack and then pop it off. So it shows the correct output but this stack can be used only for storing integers What if we want to create a stack of floats or doubles or strings Obviously we would not want to create a stack clause for each type instead we can use clause templates So let's convert this clause into a clause template The clause declaration will begin with template keyword and the type name as usual and the array will be of type t and this would change to t because it could also be a user defined type we should pass by constant reference and top would be const t ampersand Now when we have to create an instance of this stack we have to specify the type in the template argument list so if you want to create a stack of integers you will specify the type int here To create a stack of floats you can specify the type here So you see how easy it is to use the same stack clause for different types Obviously the compiler will instantiate the stack clause for all the types that it is instantiated for Note that only those member functions are instantiated that are invoked If we do not invoke some of the member functions then they are not instantiated Clause templates can also accept non-type template arguments So we can specify the size of the stack from here and this size is constant and we can use it as size of this array and when we create the instance of the stack we can specify the size here itself So this will become a stack of 10 floats This is much better than using a constant value in M-Buffer Because with this non-type template argument we can decide what should be the type of stack at compile time But we do that without hard coding the value inside the clause So far we have defined all the member functions in the clause itself If you define the member functions outside the clause then you have to follow the syntax of the templates So for example if I define the pop function outside the clause then the definition should be preceded with the declaration of the template type name t and int size and the return type would be wide and then the name of the clause Now the name of the clause is not only stack the template parameters are part of the type of the clause that is why the type of the clause is not stack it is stack of t, size and then the name of the function and then its implementation So remember in templates the template parameters are part of the type But there is one more thing Let's create a factory method for this stack clause A factory method is a method that creates instance of the clause So we'll create a function which is a static function that will return instance of the stack clause We'll call this create and this would create an instance of the stack So here we need to specify the type that is t and the size This would create a temporary object stack which we are then returning by value and we can use it like this The object that create returns is an object of a clause of type stack float comma 10 If the size here is different then these are not the same types because as I mentioned earlier the template parameters are part of the type of the clause or the function So the instance returned by create is different from this type Let's change it back to 10 Now we would also implement the create function definition outside the clause So the implementation would be as usual we'll have to proceed the definition with the type of the template The return type is stack then stack of type t size and then create Then we can copy this code and put it here Now this code will not compile The reason is the compiler doesn't know what this type is Earlier when we had defined the function create inside the clause since the name was occurring inside the clause it did not need the complete template parameters This is known as the shorthand notation In shorthand notation you do not need to specify the template parameters and you can write shorthand notation only if that type occurs inside the clause definition You cannot write shorthand definition outside the clause So you'll have to use the complete type of the stack clause here If we try to build this the compiler complains says use of clause template requires template argument list and that is for this name So that's why the shorthand notation works only inside the clause not outside it so here we have to specify all the template parameters and this compiles fine We would also implement a copy constructor for this clause and we would like to perform the copy of the attributes manually So here we can use the shorthand notation for the stack clause We'll also have to provide a default constructor We'll let the compiler generate one for us and we'll also like to initialize top before the for loop and since top starts from minus one we will use less than equal to top here and then we'll create a copy of the stack and instead of s we'll use s2 here run it and it works fine So you must have noticed that the argument to the copy constructor uses the shorthand notation The reason is it appears within the clause you can also use the longhand notation but that is not required If you define this copy constructor function outside the clause then would you need the shorthand notation or the longhand notation for this stack type You can try it out for yourself and you can let me know in the comments That's all for now In the next video we'll look at some more features of clause templates. See you later Hi, welcome to the next part on templates. In this part I'll explain explicit specialization of clause templates I have written a clause called as pretty printer that can be used to pretty print some data To use this clause we'll have to create its instance and store the data in it that we want to pretty print So the data type will be a template type and we will initialize this data through the constructor and then in print we'll simply print the data Since we are pretty printing let's add some decoration to the data that is printed, maybe we can use braces and we'll also create one more function called as get data that returns the pointer to the data and we can use this for different types So let's create instance of pre-printer for integer type and we'll pass the address of the data then we'll call print and we can also create a pretty printer for float and then print. Let's run it and see the output and this is how it looks like Perfect. Let's use pretty printer for more types Assume we also want to print a string. So we want to pre-print the string through the pretty printer class So we create instance of pre-printer and we specify the type as char star and pass p here and then we'll call print We'll comment out the earlier code Let's build this and it gives an error. So the error is cannot convert argument one from char star to char star star The problem here is when we use char star as a template parameter then the type of t is t star The declaration here becomes a pointer to pointer that is t star star and this also becomes t star star So the argument that we have to pass to the constructor would be pointer to a pointer but we are only passing a char star which is just a pointer So this would not compile So that is why we may have to pass this as a pointer to pointer and then let's build it It builds fine let's run it and it prints the correct data Let's also try to use the function getData So we want the data back from the pre-printed class and when we build this it gives a compile time error The reason being because we use the template parameter as char star the type of t is t star and the getData now returns t star star So you see all this becomes counter-intuitive when we use pre-printer with char star type On the other hand what we could have done is we could have just specified the type as char and we would just pass the pointer here So obviously the type of t would be only t star and if we build this this works we can get the data back from the pre-printer class without any change in the syntax Let's run this Unfortunately it only prints the first character and not the entire string The reason for printing only the first character is because m underscore pdata is a pointer to a string and when we apply asterisk on it we dereference it so it would only print the first character So you see using the pre-printer with a string type does not give correct results but the pre-printer class works for other types In this case we can explicitly specialize pre-printer for the string types So let's go ahead and create a specialization for the string types So we'll create a copy of this and as we learned the specialization of function templates when you create a specialization the template argument list has to be empty and you need to specify the type here So this becomes explicit specialization of the pre-printer class for the char star type and now this is going to be char star this would also be char star and we will remove the asterisk from here and this would be replaced by char and we'll also have to change this to char star Let's run Now it correctly prints the string So the explicit specialization now works for the string types that is char star types That's all for now In the next video we'll see some more examples of explicit specialization See you then We will continue our discussion about the explicit specialization of class templates Let's try pre-printer with a vector We'll create a vector of integers and we want to pre-print the data inside the vector So we'll create a pre-printer for vector type of integers and then let's print we build and it doesn't work because mndescore pdata is a pointer to the vector and when we apply asterisk on it it will return the vector object itself and the insertion operator is not overloaded for that class So obviously we can also explicitly specialize this class for the vector type and we can do that very easily by creating the specialization like this This is going to be a vector of type int and this would also be a vector of type integers and instead of printing the vector like this we will use a range based for loop and this is how the print function will look like and this also needs to be changed to vector of type int Let's build this again and run it and now you can see it correctly prints the data that is inside the vector If you compare the explicit specialization of the pre-printer class for vector and caster you will notice that for caster we had to specialize the entire class because even getData was not working when we used pre-printer for a caster because the type of t was becoming t star star and the return type was getting resolved to t star star However for vector that is not a problem The problem was only with printing So instead of specializing the entire class for vector we could have just specialized the member function print So we will write a specialized version of the print function for the vector type and to do that we just have to write the definition of that function outside the class not inside the class Obviously the definition would start with template with empty template argument list the return type is wide then the name of the class and here we specify the type for which we are specializing this function and we can copy the code that we wrote earlier over here and we don't need this specialization for the vector and let's build this run it and it works for the vector type Note that when you explicitly specialize of member function template then its definition should be in the namespace scope that means you cannot write the definition of this explicit specialization inside the class it has to be outside the class That's all for now In the next video I will explain partial specialization See you then and welcome back to the next part of templates In this video we will learn about partial specialization of class templates In partial specialization we specialize some of the template parameters in a class So the difference between explicit specialization and partial specialization is that in explicit specialization all the template parameters are specialized but in partial specialization only some of the template parameters are specialized So we will go back to our old example where we created the class pretty printer we would add one more template parameter and this would be a non-type template parameter and we will call this columns Imagine the columns contains a value that is used by the print function to find out what is the available width to print the data So we can print the value of the columns here and obviously this value is going to be constant, we cannot modify it and then we can create the instance of pretty printer for any type we will also need to create some kind of data and then we invoke print let's run it and you can see it shows the columns available are 14 assume that this class is used on devices where the number of columns are 80 and this is the most common number of columns available on these devices So we would like to take advantage of this number and print the data in the print function in a more efficient way or we can format it in a better way In that case we would like to find out when the value of columns is 80 and then accordingly we can decide to print the data in a different way Obviously we can do it at runtime, we can have conditional statements in print function and then we can check for the value of columns but there is a more efficient way We can partially specialize the pretty printer class for the value of columns So in partial specialization we will specialize some of the values of the template parameters Since we are specializing the second non-type parameter the first parameter will be type name and when we create the class pretty printer we will have to specify the type name that is the first template argument and then the value of the second argument which is going to be 80 and then based on this we will decide how to implement the functions of the class So we can create a copy and we will change this message this variable will not be available and let's change this to 80 and when we run this you can see the compiler has chosen the partial specialization Let's create one more class In our operator overloading lectures we discussed a concept of RAII which is used for creating smart pointers and we had implemented a very basic smart pointer for integer pointers Now with the knowledge of templates we can create a smart pointer that can work with any type Let's call this smart pointer to contain a constructor through which we initialize this member and then remember we had overloaded the arrow operator and we also overloaded the asterisk operator to return the value at address and finally the destructor which will free the memory of the internal pointer and then we can use this smart pointer and it will automatically free the memory at the end of the scope and we can print the value using the asterisk operator let's run it and you see the value 3 What if we want to use smart pointer with a dynamic array So instead of this being a simple integer pointer will make this as an integer array So smart pointer now holds a pointer to an array and the class will not give correct semantics and behavior for smart pointer when it internally points to an integer array First of all we cannot access the elements of the array using this smart pointer Secondly, the implementation of the destructor for an array is not correct it should use the delete for arrays so in this case we can partially specialize the smart pointer class for array types and we can do it like this We'll create a copy of the smart pointer and here we will specialize this class for array types So this indicates whenever the smart pointer class is instantiated with any type but if it is an array then this specialization will be used and obviously the arrow and asterisk operators do not make sense for an array so we will remove these and instead replace with the overloaded subscript operator and in the destructor we will use the correct form of delete to free the memory and when we create the instance of smart pointer that is going to hold an array then we specify the argument as an array so this will indicate that the smart pointer is being instantiated for an array type and obviously the compiler will choose the partial specialization You can see star S1 does not work here instead we can use the subscript operator and the same operator can be used for reading and writing and this should be index and when we run this it works and you can see the correct value being printed through the cout statement The smart pointers in the standard library also provide partial specialization for the array types that's all for now in the next video we will look at some more C++11 features of templates see you later Hi and welcome back to the next part on templates in this video I will explain type aliases but before that I would like to revisit type definitions type definitions can be created through the keyword type deck and using this keyword we can introduce a new name for an existing type the new name then becomes a synonym of that type this way it is useful to construct a shorter or more meaningful names for existing type type def also helps us to simplify declaration of some types especially with function pointers and templates using type definitions it is possible to hide the implementation details of the types this way we could change the type of the type definition without effecting the client code C++ standard library itself provides type definitions for many standard classes such as stream classes, string class etc for example when we use the string type in our code we are actually using a type definition of a class called basic string so the type definitions reduce the complexity of using the type but when creating a type definition it does not introduce a new type it only introduces a new name for existing type these are some examples of type definitions type definitions are commonly used for representing unsigned integers so instead of writing unsigned int we just have to write the type def uint the same goes for long long type while creating long long types the declaration seems too long pun intended so we can reduce it to a type def called as L long type definitions also help us reduce the complexity of using templates in this example I have created a vector of list of employees and the type definition is called as teams instead of using the complete declaration which includes the vector list and the employee type we can use the type def when creating the instance of to iterate even then we can use the type def so you see the complexity of the code is reduced but obviously in C++11 we would have preferred to use auto here type definitions also can be used to create type definitions for function pointers this is especially useful when function pointers appear as arguments into functions or when they are used as return types C++11 introduced a new feature called as type alias type alias also creates a name that is a synonym of an existing type as with type definitions it does not introduce a new type and it is exactly similar to that of a type def declaration to create a type alias we use the using keyword the using keyword requires an identifier and you can specify the type on the right hand side then the identifier will act as a synonym for that type these are some of the examples of usage of type alias note that using the type alias seems more natural because this is consistent to the way we initialize variables so in the first example uint is an alias of unsigned int the second example is llong that is an alias of long long type in the third example the teams is an alias of a vector of list of employees and finally the last example shows how we can create an alias for a function pointer type the errorfn type is a type that represents a function pointer that accepts an integer and returns a const car star let's try out these examples in visual studio I have already written some code so there is a function called getErrorMessage that accepts an error number and returns the string associated with that error number so for the time being we are just returning the string called empty and showError is a function that accepts a function pointer that accepts an integer and returns a const car star so obviously we can pass the address of getErrorMessage in the showError function but look at the declaration of showError function it's quite complex due to usage of function pointer we can reduce the complexity using the type definition or a type alias if we use the type definition then we can create it like this now pfn is a type that represents a function pointer so instead of writing the entire declaration here we can just write the type name and a variable and we can do the same thing here we can invoke showError by passing the address of the function we forgot asterisk here using type aliases we could have created the function pointer type like this using pfn equal to const car star then in the brackets we have to write asterisk that represents a function pointer type this pfn is an alias of this function pointer type if you compare the two ways through which the alias is created the second seems more natural so we will comment out the type def declaration now if we use type definitions with templates like this let's say this is list of names obviously this is quite a long declaration so we might want to create a type definition for this so that we can use this type in many places in our program without having to resort to writing this long declaration so we can create a type there and this could be replaced with names the disadvantage of using type def with templates is that type definitions cannot be templatized that means if I want to use names in some other context let's say I want to create a vector of list of players then I cannot templatize this with a template parameter such as player but using type aliases we can create an alias template so if we want to use this type in different contexts but we should be able to templatize the type here then we can use alias templates and we do it like this template type name t and using names equal to and then here we'll specify the type name t now when you use names you can use it in context of names for any entity so it could be a string or you could also use it with some other type let's say you want to have names of names you can even do that and this could be string you may also permanently bind a type in this list so if you want to bind it with strings then you can do it like this and obviously you cannot templatize this with any other type and we'll remove this line so you can see that alias templates have a clear advantage over type definitions this is the end of the section for templates in the next section I'll start with lambda expressions see you there