.. highlight:: c++ :linenothreshold: 5 **************** Templates in C++ **************** Template is a method in C++ language to generalize functions/classes to be used with various base types. It allows a same function or class to work with various data types. + E.g. STL containers like ``vector``, ``vector``, ``set``, ``map``, etc. are templates. + E.g. type cast functions like ``static_cast(3.5)`` + Similar idea to Java generics but with different implementation details Pro and Con =========== + Pro * Generalize functions/classes to work with various data types * Avoid code redundancy * Allow type-safe operations + Con * Code bloat The compiler will generate a copy of the function/class for each base type used. This may cause the executable file to be very large. * Hard to debug The error messages from the compiler may be hard to understand. * Compilation The compilation can be very slow if the template is used in many places. * Header-only library It violates the principle of separation of declaration and implementation. Alternatives ============ There are other way to allow generalization of functions to work with different types. + function overloading * Define many functions like ``min(int, int)``, ``min(double, double)``, ``min(char, char)``, with a same name but different parameter types. * Similar logics may be repeated many times. + implicit type casting * A function ``min(double, double)`` can handle calls like ``min(2, 3)``, ``min(2.0, 3)``, ``min('a', 66)``, etc. * May cause confusion * Not work with complex data types Sometimes the base types are not used as parameter types but internal variables. In these cases, template is the only solution. Syntax ====== + ``template`` keyword * ``template `` clause. * The list of template parameters can contain one or more template parameters separated by commas. * The template parameters are enclosed in angle-brackets (<>). * We only focus on **type template parameters**, which is used to match a type. - Started with keyword ``class`` or ``typename`` (interchangeable) * The ``template`` clause must be above the following entities when every the parameterized type is used. - function declaration - function definition - class declaration - method definition :: template void func1(T param1); template class MyClass{ public: MyClass(T param1); }; // important to notice that the first MyClass is followed by here! // MyClass is the name of the template while MyClass is the // name of the class template MyClass::MyClass(T param1) { // core logics } + To use a template * Specify the template argument to **instantiate** the template:: // function template int result = func1(10); // class template MyClass obj1; if (obj1.method1(10)) cout << "True" << endl; * **Template argument deduction**:: func1(10); // T will be int func1<>(10); // T will be int func1(10.0); // T will be double MyClass obj1(10); // T will be int .. note:: Template argument deduction for class template not available until C++ 17 Split header/implementation file ================================ **TL;DR Put everything of a template in a single header file.** Single Header File ------------------ A template must be instantiated to provided the compilable source code. The instantiation of templates is lazy. Only when a templated entity is used with base type(s) specified, the template will be instantiated. For instance, ``vector`` is an instantiation of the ``vector`` template. When an occurrence of ``vector`` is seen by the compiler, the compiler will search to see if there is a version of it. If not, the compiler will look for the source code of the ``vector`` template and instantiate it to generate the instance of ``vector``. - a template itself cannot be compiled - a template must be instantiated to generate code to be compiled - instantiation is a process to provide really parameter to a template - when you instantiate a template in a cpp file and try to compile it, the compiler will look for the template to instantiate from the current cpp file, and all included header files, etc. - if the template code (not instantiated) is in another cpp file, the compiler cannot find it and the instantiation will fail. Thus, it is natural to have a templated function/class in a single header file so that when a cpp file uses the template, the compiler can find all the template code in the header. This method is recommended in our courses. Split To Multiple Files ----------------------- **Optional Contents** Templates requires special organization of the source code because the compiler needs to see both the template definition (not instantiation) and its use within a single translation unit. Thus, it is not possible to split the template into a header file and a cpp file like normal modular code. There are two work-around methods when the split is preferred: 1. split implementations in a separate text file and include it in the header file after the declarations. This method is not recommended but commonly seen in old-fashioned courses. 2. split implementations into a cpp file, explicitly declare all possible instantiations of the template in the cpp file; Thus, when other cpp needs to use the template, the instantiated codes have already been instantiated and ready for use. .. _example: Examples ======== .. code-block:: c++ :caption: Template Syntax template T func1(T param1); template class MyClass{ public: bool method1(T param1); } // important to notice that MyClass is followed by here! template bool MyClass::method1(T param1) { // logics return true; } .. literalinclude:: template-demo.hpp :caption: template-demo.hpp :language: cpp :linenos: .. literalinclude:: template-test.cpp :caption: template-test.cpp :language: cpp :linenos: