C++ 11 Variadic Templates

Discussion in 'C++' started by BiplabKamal, Feb 16, 2016.

  1. In C++ we already know different uses of the ellipsis operator (…) in different context. It is used for functions with variable number of argument, variadic macros and in catch block in exception handling. Let us look into these existing meaning of ellipsis operator (…) in different context and then we will see what new meanings of ellipsis operator (…) called variadic templates, C++11 added in its list of new features.

    1. C style function with variable number of arguments : Both C and C++ programmers know about the C standard library functions. Probably C standard library function printf() is the most famous function known to C and C++ programmers. The signature of printf() function is :

    int printf(const char* format, ...)

    Here the printf() function takes one or more number of arguments. Similarly

    void vFn(...); takes zero or more number of arguments. So ellipsis (...) here is used to specify that a function can accept any number of arguments of arbitrary types including zero argument.

    2. Variadic Macros:Ellipsis mechanism also applicable for per-processor in the form of macro. Here is the example :

    #define MYMACRO(...) Somefunction (stderror, __VA_ARGS__)

    Above macro takes zero or more arguments. __VA_ARGS__ is replaced by the all arguments that match the ellipsis, including commas in between them

    3. Generic catch block catch(...): Ellipsis has different meaning when used in catch block. This is used in last catch block where any exception uncaught in previous catch blocks will be caught. Here is an example:
    Code:
        try
        {
            // Do other things
            throw "Sme error";
    
        }
        catch (int)
        {
            cout << "Int Exception Caught"<<endl;
        }
        catch (...) // Exceptions other than int will be caught here
        {
            cout << "Unknown exception caught"<<endl;
        }
        
    C++11 added a new utility of the ellipsis (...) operator in the context of function template and class template which known as Variadic Template:

    4. Variadic Template(...): Variadic template works similar to C-Style functions with variable number of arguments. Now function templates and class templates can be declared with arbitrary number of template arguments.

    Variadic Function Template:



    Let us look into the following function template declaration:

    template <typename T,typename ... tempargs>
    void TemplateFn(T arg1, tempargs ... args);

    In the above function template the meaning of ellipsis says that it has the first argument followed by zero or more number of arguments. That means when instantiating a template function from this function template you can specify one or more number of template parameters. For example following are the template functions invocation for the above template:

    class Myclass;
    TemplateFn<string, string, int, Myclass>("Biplab", "Address", 40, Myclass());

    Above template function call specify that the first argument is string and there are three arguments for variable argument list. Using type deduction feature you can also invoke the template function like:

    TemplateFn("Biplab", "Address", 40, Myclass());
    TemplateFn(100,200,"Some string");

    The content of the variable argument list is called Parameter Pack. Parameter packs are unpacked in the body of the function.

    Function template also can have only the parameter pack without:

    template <typename ... tempargs>
    void TemplateFn(tempargs ... args);

    The function accepts zero or more arguments shown below;

    TemplateFn(100,200,"Some string");
    TemplateFn();

    The equivalent function templates for 1st invocation above will be:

    template <typename T1,typename T2,typename T3>
    void TemplateFn(T1 arg1, T2 arg2, T3 arg3);

    And the function signature will be:

    void TemplateFn(int arg1, int arg2, const char* arg3);

    and the 2nd one will be a function without any argument:
    void TemplateFn();

    You can specify default value for template arguments which are not parameter packs. In that case you may not specify a new type argument and the default type will be used. For example:

    template <typename T = int,typename ... tempargs>
    void TemplateFn(T arg1, tempargs ... args);

    And function call like TemplateFn("Some Name"); will deduce the template function as:

    template <typename T>
    void TemplateFn(T arg1);

    And the final function signature will be:

    void TemplateFn(const char* arg1);

    Variadic Class Template:



    Like variadic function template you can create variadic class template like:
    Code:
        template<class T, class ... temnpargs>
        class TemnplateClass
        {
        public:
            TemnplateClass(T arg,temnpargs ... args);
            //Class body
        }
    
    While instantiating the class template you must specify at least the first argument and followed by zero or more arguments. args is the parameter packs which will be unpacked in the class functions. Following lines of code will instantiate the class template:

    TemnplateClass<string> someobj( "Biplab ");//No argument for the parameter packs
    TemnplateClass<int,string,char> anotherobj(20, "Kunal ",'c'); //Two arguments for the parameter packs

    If default arguments are used then you can instantiate it by specifying no argument

    template<class T =int, class ... temnpargs> class TemnplateClass;
    TemnplateClass<> someobj();//No argument for the parameter packs
    TemnplateClass<string,string,char> anotherobj();//Two arguments for the parameter packs

    Parameter Pack expansion:

    We got to know about parameter packs in the form of <typename ... params> as a list of variable number of type arguments separated by commas. When class uses the parameter pack in a method argument the syntax becomes like <params ... args> where args is the list of template parameters. We also came across parameter packs for both class template as well as function template. What we are yet to look into is how those parameter packs are expanded in the body of the template. When expanded it gives the template parameter list. To expand the parameter pack ellipsis(...) has to be put after the pack name like args....

    Note the sequence of usage of variadic arguments:
    • In the template specification it is : <typename ... params>
    • In the template function argument it is : <params ... args>
    • In the function definition it is : args...
    Bellow is a sample program to demonstrate the expansion of parameter packs

    Code:
    template<typename T, typename ... parampack>
    class MyTemnplateClass
    {
    public:
        MyTemnplateClass(T arg,parampack... args)
        {
            cout << "Type of first template argument: " <<typeid(arg).name()<<" and value is: "<<arg << endl;
            cout << "Paramet pack expansion: ";
    
            ShowPack(args...);
            cout << endl;
            
        }
        template<typename T2,typename... params>
        void ShowPack(T2 arg1, params... arglist)
        {
            ShowPack(arg1);
            ShowPack(arglist...);//Parameter expansion
        }
        template<typename T3>
        void ShowPack(T3 arg)
        {
            cout << arg << " ";
        }
        void ShowPack()
        {
            cout <<endl;
        }
    };
    void PrintPack()
    {
        cout << endl;
    }
    template <typename T>
    void PrintPack(T arg1)
    {
        cout << arg1 << " ";
    }
    template <typename T, typename ... tempargs>
    void PrintPack(T arg1, tempargs... args)
    {
        cout << arg1 << " ";
        PrintPack(args...); //Parameter expension
    }
    template <typename T, typename ... tempargs>
    void TemplateFn(T arg1, tempargs ... args)
    {
        cout << "Type of first template argument: " << typeid(arg1).name() << " and value is: " << arg1 << endl;
        cout << "Parameter pack expansion: ";
        PrintPack(args...);
    }
    
    int main()
    {
        cout << "Creating Template class object" << endl;
        MyTemnplateClass<int,char,float,int> obj(100,'m',30.7,40);
        
        cout <<endl<< "Calling Template Function" << endl;
        TemplateFn(10,200,'c',20.5,20);
        cout << endl;    
    }
    
    Output:

    Creating Template class object
    Type of first template argument: int and value is: 100
    Paramet pack expansion: m 30.7 40

    Calling Template Function
    Type of first template argument: int and value is: 10
    Parameter pack expansion: 200 c 20.5 20


    Template programming requires to handle complexity more than the complexity involved in normal programming. Variadic template makes it even more complex. So unless you have clear view of how the compiler processes the templates in different scenario and what code it generates it is not possible to use this feature effectively. As normal class or function are specifications(or template) of an runtime object or behaviour, class template or function template is a specification of a class or function. Instance of a template is a class or function generated by the compiler based on the input(template arguments) provided in the instantiation code. Variadic template is the specification of a template again instantiated by the compiler based on instantiation inputs. Take the following example of a function like C library function printf():

    The variadic function template specification is:

    template<typename ... Types>
    void Print(std::string format, Types... args)
    {
    // Code here
    }


    If the invocation of the function looks like:

    Print("My name is %s",std::string("Shabbir"));

    Then the compiler first resolves the number of template arguments which becomes like:

    template<typename T1,typename T2,typename T3>
    void Print(std::string format, T1 arg1, T2 arg2,T3 arg3)
    {
    // Code here
    }

    Then the compiler resolves the type of T1, T2 and T3 which are:

    T1 = std::string;
    T1 = int;
    T1 = int;

    So the concrete function prototype looks like:

    void Print(std::string format, std::string arg1,int arg2,int arg3)
    {
    // Code here
    }

    We can say that variadic template generates templates, template generates functions or classes and class and function generate class objects or function objects. First two happens before compilation and the last is the part of compilation process. Normally the programmers are habituated to visualize run-time objects but to be good in template programming you need to visualize the pre-compiled classes or functions generated by the compilers.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice