1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

C++ Templates

Discussion in 'C++' started by BiplabKamal, May 9, 2016.

  1. BiplabKamal

    BiplabKamal New Member

    Joined:
    Dec 30, 2015
    Messages:
    36
    Likes Received:
    5
    Trophy Points:
    0
    Occupation:
    Software Engineer
    Location:
    Kolkata
    Before we go into the details of C++ templates let us take an example of function overloading. I am assuming you know what is function overloading.

    Code:
    #include<iostream>
    using namespace std;
    int larger(int n1, int n2)
    {
        cout << "int larger(int, int) is called" << endl;
        return n1 > n2 ? n1 : n2;
    }
    char larger(char n1, char n2)
    {
        cout << "char larger(char, char) is called" << endl;
        return n1 > n2 ? n1 : n2;
    }
    
    int main()
    {
        int n1{ 10 }, n2{ 20 };
        char c1{ 'a' }, c2{ 'b' };
        cout << larger(n1, n2) << " is larger" << endl;
        cout << larger(c1, c2) << " is larger" << endl;
        return 0;
    }
    
    In the above program there are two overloaded functions with different argument types and return values. If you notice you will see that the code or logic in both the function are exactly same except the type of the arguments and return type. So don’t you think this is redundant coding? And suppose instead of two overloads if we need many more overloads for different types like int, float, char, double, string, and user defined classes. Won’t it be very helpful if we have a type independent code which will accept the type as parameter and generate type dependent code? This is exactly what template is. C++ template allows you to write generic code which will generate the actual code when you use the generic code for specific types. Look at the following code which will do the exactly same thing as the above code does:

    Code:
    #include<iostream>
    using namespace std;
    
    template<class Type>
    Type larger(Type n1, Type n2)
    {
        const char* type = typeid(Type).name();
        cout << type <<" larger("<< type <<", "<< type << ") is called" << endl;
        return n1 > n2 ? n1 : n2;
    }
    
    int main()
    {
        int n1{ 10 }, n2{ 20 };
        char c1{ 'a' }, c2{ 'b' };
        cout << larger<int>(n1, n2) << " is larger" << endl;
        cout << larger<char>(c1, c2) << " is larger" << endl;
        cout<<larger<double>(4.5, 20.8)<< " is larger" << endl;
        return 0;
    }
    
    The output is:
    Code:
    int larger(int, int) is called
    20 is larger
    char larger(char, char) is called
    b is larger
    double larger(double, double) is called
    20.8 is larger
    
    Note that above program has reduced the code from multiple function to one function and also increased the coverage to unlimited types. Suppose we have a class City as shown below:
    Code:
    class City
    {
        int Area;
        int Population;
        string Name;
    public:
        City(string name, int area, int population) :Name{name},Area { area }, Population{ population } {}
        bool operator >(const City& ct)
        {
            return Area > ct.Area ? true : false;
        }
        bool operator <(const City& ct)
        {
            return Area < ct.Area ? true : false;
        }
        int GetPopulation()
        {
            return Population;
        }
        int GetArea()
        {
            return Area;
        }
        int GetPopulationDensity()
        {
            return Population / Area;
        }
        const string& GetName()
        {
            return Name;
        }
    };
    
    We can use the class City to call the above template function. Look at the modified main() function:
    Code:
    int main()
    {
        int n1{ 10 }, n2{ 20 };
        char c1{ 'a' }, c2{ 'b' };
        City Kolkata{"Kolkata", 10000,10000000 }, Delhi{"Delhi", 9000,8000000 };
        cout << larger<int>(n1, n2) << " is larger" << endl; // Will instantiate the template  
        cout << larger<char>(c1, c2) << " is larger" << endl;// Will instantiate the template
        cout<<larger<City>(Kolkata, Delhi).GetName().c_str()<< " is larger" << endl;// Will instantiate the template
        return 0;
    }
    
    The output is :
    Code:
    int larger(int, int) is called
    20 is larger
    char larger(char, char) is called
    b is larger
    class City larger(class City, class City) is called
    Kolkata is larger
    
    That’s what the template s. Template is generic code which is used by the compiler to generate actual code. Generating actual code is called template instantiation. This is not a run-time activity but compile time activity. Template will have one or more arguments which is type information. The arguments of a template is the placeholder of types used in the template code. Instantiation is made by passing type arguments to the code. The compiler replaces the place holders of types with actual types and create a new temporary code to compile. This leads to generating a temporary version of code for each different types. Compiler only compiles the generated code and not the actual template. In the above example there will be three function generated. Here is one of them when we pass int type as argument
    Code:
    int larger(int n1, int n2)
    {
        const char* type = typeid(int).name();
        cout << type << " larger(" << type << ", " << type << ") is called" << endl;
        return n1 > n2 ? n1 : n2;
    }
    
    So template is generic code used by the compiler to generate code compilable code. Generation of code is called template instantiation. While instantiating you need to specify the argument’s values which are type identifiers. C++ supports template for functions as well as classes. Functions include member function as well as non-member function. The example we saw is non-member function template. Template for member function is similar to non-member function template.

    A template definition starts with the keyword*‘template’*followed by template parameters inside *< >*which is followed by class/function declaration.
    Code:
    template <class T1, class T2>
    T1 template_function(T2 p)
    {
        //Code
    };
    template <class Type>
    class template_class
    {
        //members deceleration/definition
        ...
        template <class T>
        void DoSomething(T val);
    };
    
    In above code,*T1, T2 and Type are template arguments and*‘class’*is a keyword. Another keyword*‘typename’*can be used instead of class.

    Template function or class is instantiated when you call the function or create an instance of the class with specific types as template parameters. template parameters are specified inside *< >*after the class or function name. Following code is instantiating template function and class:
    Code:
        template_function<int, char>('c'); // Instantiating of template function
        template_class<int> obj; // Instantiation of template class 
    
    I mentioned above that compiler does not compile a template function but instances of themplates. That is true only for function or class definition. Compiler does check the declaration part of the function or class for syntax even if you don’t use it. For example the compiler will not generate any error for incorrect syntax in the code inside the template function or member function of template class unless you call it. For example following code will not generate any error if you don’t call the functions:
    Code:
    template <class T1, class T2>
    T1 template_function(T2 p)
    {
        abcd; // incorrect
        //Code
        return p //incorrect
    };
    template <class Type>
    class template_class
    {
        Type x;
    public:
        void f1()
        {
            abcd;//incorrect
        }
    
    };
    
    Automatic type deduction works for temple function. Types can be automatically deduced by the type of parameters passed to the function if template arguments are not given. For example, in the instantiation of above template function T2 can be derived from the type parameter passed to the function. template_function<int>('c'); Here T1 is specified as int but T2 will be deduced to be char type based on the function parameter ‘c’.

    Note that only those template arguments can be derived automatically which are used in function argument list.

    Template specialization

    There is an interesting feature of template called template specialization. This is helpful when the logic is same for most of the type but different for few specific cases. Template specialization helps to write specific code for specific template arguments. For example if you want handle the template_function<int, char> in a different way then template specialization like following code is required. Specific types argument have to be mentioned within <> after the function name and actual and actual types should be replaced in the function declaration and definition. Here is an example of template specialization-
    Code:
    template <>
    int template_function<int,char*>(char* p)
    {
        return *p;
    };
    
    Note that in case of template specialization the code is compiled even if you don’t call it. This actually behaves like a non-template code.

    We have used template function and template class in the examples above. Let us see an example of template member function and its specialization. This program will sort a list of objects and will work for different types of objects. Merge sort is used but logic to compare two objects is passed from the client-
    Code:
    #include <iostream>
    #include<vector>
    using namespace std;
    // generic functor to compare two object
    class Compare { 
    public:
        template<typename Type>
        bool operator()(Type n1, Type n2)// member template
        { 
            return n1 > n2 ? true : false;
        } 
        // template specilization for std::string
        template<>
        bool operator()<string>(string s1, string s2)
        {
            cout << "string version is called" << endl;
            return s1.compare(s2)>0 ? true : false;
        }
    };
    //Generic function to sort a vector of objects. Using merge sort
    template<class T>
    void Sort(vector<T>& v, Compare c)
    {
        int size = v.size();
        if (size <= 1)//it is sorted
            return;
        int middle = size / 2;
        vector<T> first, second;
        for (int i = 0;i < middle;++i)
            first.push_back(v[i]);
        for (int i = middle;i < size;++i)
            second.push_back(v[i]);
        Sort(first, c);
        Sort(second, c);
        Merge(first, second, v, c);
    };
    template<class T>
    void Merge(vector<T> v1, vector<T> v2, vector<T>& v,Compare c)
    {
        v.clear();
        while (!v1.empty() & !v2.empty())
        {
            if (c(v1[0], v2[0]))
            {
                v.emplace_back(v2.front());
                v2.erase(v2.begin());
            }
            else
            {
                v.emplace_back(v1.front());
                v1.erase(v1.begin());
            }
        }
        for (T item : v1)
            v.push_back(item);
        for (T item : v2)
            v.push_back(item);
    }
    
    int main()
    {
        //Sort a integer list
        vector<int> v{3,3,7,1,5,6,9,34,6,78};
        Sort(v, Compare());
        cout << "Sorted numbers: ";
        for (int i : v)
            cout << i << " ";
        cout << endl;
        // Sort a string list
        vector<string> vs{ "abcd","xyz","acbd","mnop" };
        Sort(vs, Compare());
        cout << "Sorted strings: ";
        for (string s : vs)
            cout << s.c_str() << " ";
        cout << endl;
    }
    
    Output:
    Code:
    Sorted numbers: 1 3 3 5 6 6 7 9 34 78
    string version is called
    string version is called
    string version is called
    string version is called
    string version is called
    Sorted strings: abcd acbd mnop xyz
    
    Default value for template arguments

    Like default value for function argument template arguments also can have default value and syntax is also similar to function declaration with default parameters. Default values can be specified for last arguments of the argument list. For example following template class is having one default argument and that must be the last one in the argument list.
    Code:
    //template class with default argument
    template<class T1, class T2=int>
    class templateclass
    {
        //decleration here
    };
    
    Now you can instantiate the template with only one template argument if you want :
    Code:
    templateclass<char> obj;
    
    Nested Template

    Templates can be defined withing a class or a template class. Template within a class is called member template. Member template can be a function or a class. We have already discussed member template as function called template member function. Member template as class is called nested template. You can declare a template class local to a class scope and can define the nested template within the outer class or outside the outer class. When you define it outside the outer class they must be prefaced by both the outer class template parameters(if the outer class is a template class) and the member class template parameters. Here is an example:
    Code:
    #include<iostream>
    template<class T>
    class OuterClass
    {
        T value;
        template<class U>
        class NestedClass
        {
            U value;
        public:
            NestedClass(U);
            void Show();
        };
        NestedClass<T> nc;
    public:
        OuterClass(T val) :nc{ val } {}
        void Show()
        {
            nc.Show();
        }
    
    };
    template <class T>
    template <class U>
    OuterClass<T>::NestedClass<U>::NestedClass(U val)
    {
        value = val;
    }
    template <class T>
    template <class U>
    void OuterClass<T>::NestedClass<U>::Show()
    {
        std::cout << value<<std::endl;
    }
    int main()
    {
        OuterClass<int> oc(100);
        oc.Show();
    }
    
    Variadic template

    Template function can have variable number of arguments (since C++11). Template with variable list of arguments is known as variadic template .Variadic template arguments are specified using elipsis (…) as shown below
    Code:
    template <typename T,typename ... tempargs>
    void TemplateFn(T arg1, tempargs ... args);
    
    args is called parameter pack which is unpacked inside the function(using args…). Variadic Template is discussed in detail in another article.

    Template variable

    So far we have discussed about template function and template class. The latest version of C++, C++14 also introduced template variable. With template variable a single variable can hold different type of values. Here is an example:
    Code:
    #include<stdio.h>
    template<typename T>
    constexpr T pi = T(3.1415926535897932385);
    
    // Usual specialization rules apply:
    template<>
    constexpr const char* pi<const char*> = "pi";
    
    
    int main()
    {
        printf("%d", pi<int>);
    }
    
    Template meta programming

    I think templates are the most complex thing in C++ and template meta programming is more and more in that direction. I would not like to go into that complexity level but can not resist from giving some hint about what it could be.

    In fact the aim of design of template in C++ was to write parametrized generic code for compiler. But it turned to be a higher order programming tool which enabled compiled time programming or meta programming. We will call it template meta programming. In fact the power of template programming is limited by the capability of the programmer to handle complexity. Simple templates generate code during compilation. But you can write template which will generate template. You can write template which will take another template as input. All these features are possible due to template specialization. You can do any computing which is computable using templates and these are compile time computation. Compile time programming means compiler will generate the output of the computation during compilation. I will just give a head start with an example where we can calculate the factorial value of a number during compile time.

    Code:
    #include<iostream>
    //template class
    template<int i>
    struct factorial
    {
        enum{val = i*factorial<i-1>::val};
    };
    //template specialization
    template<>
    struct factorial<0>
    {
        enum { val = 1 };
    };
    
    int main()
    {
        int array[factorial<5>::val]; // The array index will be evaluated at compile time to 120
        std::cout << "Size of array is: "<<sizeof(array)/sizeof(int)<<std::endl;
        return 0;
    }
    
    In the main function the index of the array will be evaluated at compile time to 120. This example of compile time computation is equivalent to a recursive function at run time. You can write compile time function, branching, recursive function and so on. Those will be too complex to discuss here. Template meta programming is not used in day to day regular programming but used in developing libraries. You will find them in C++ STL.

    The advantage of template in regular use is to avoid writing duplicate code and code optimization by the compiler. But remember templates not used are not compiled and not checked for syntax. So to test a template you have to instantiate and use it.
     

Share This Page