Extending Type function via 'auto' and 'decltype' keywords

Discussion in 'C++' started by BiplabKamal, Jan 21, 2016.

  1. To understand usage of auto and decltype keyword you need to know the existing type deduction facility available in C++98, because auto and decltype are C++11/C++14 extension to the existing feature of the compiler to deduce type from another expression. In case you are not aware of existing type deduction let us first look into the type deduction feature available in C++ prior to C++11 -

    In C++98 we find automatic type deduction by compiler when we instantiate a function template i.e. we invoke a template function or initialize a function pointer with a template function instance. While instantiating a function template we can either specify the types (template arguments) or can leave to the compiler to deduce types from the function parameters supplied. This is illustrated in following examples-

    Code:
    template<typename T1, typename T2>
    void Fn(T1 t1, T2 t2); // Function template declaration
    
    Fn<int,double>(100,10.5); // function template invocation
    void (*pFn)(int, double) = Fn<int, double>; // Function pointer initialization with function template instance
    
    //Above two lines are equivalent to following two lines. Here compiler deduce the type from function parameters
    Fn(100, 10.5);
    void(*pFn2)(int, double) = Fn;
    
    Note that if there are template parameters which are not used in the function parameter list they cannot be deduced by the compiler. Following code shows the difference

    template<typename T1, typename T2>
    T1 Fn(T2 t2); // Function template declaration
    Fn<int>(100.5); // 2nd template argument is deduced but argument need to be specified

    Template type deduction in C++ works very well. Thousands of programmers have used template functions without problem, even though many of those programmers have hard time to understand how the types used by those functions were deduced. To instantiate a function template every template argument need to be known by the compiler but it is not necessary to specify every template argument because compiler is capable of deducing missing template arguments from function arguments.

    When function arguments are passed by value it is straight forward as shown above but there is implication when the arguments passed are reference, pointer or constant types. Here are more examples of template type deduction-
    Code:
    int i = 10;
    const int ci = 10;
    int& ri = i;
    const int& cri = i;
    
    Example 1: (When function argument is value type)
    Code:
    template<typename T>
    void Fn(T param);
    
    Fn(ci); // T is deduced to be int
    Fn(ri); // T is deduced to be int
    Fn(cri); // T is used to be  int
    
    Note that the const-ness and reference-ness of the argument expression are ignored when the function argument is passed by value.

    Example 2: (When function argument is reference type)
    Code:
    template<typename T>
    void Fn(T& param);
    
    Fn(ci); // T is deduced to be const int and param type is const int&
    Fn(ri); // T is deduced to be int and param type is int&
    Fn(cri); // T is used to be  const int and param type is const int&
    
    Note that the reference-ness of the argument expression does not have any effect but const-ness has when the function argument is passed by reference. This is same if the function argument is pointer like-
    Code:
    template<typename T>
    void Fn(T* param);
    
    Fn(&ci); // T is deduced to be const int and param type is const int*
    Fn(&ri); // T is deduced to be int and param type is int*
    Fn(&cri); // T is used to be  const int and param type is const int*
    
    Introducing 'auto' keyword type deduction is extended for variable type where you don't specify the type of the variable but it is deduced from the type of initialization expression.

    Template instantiation take the following form-
    Code:
    //declaration
    template<typename T>
    Fn(T arg);
    
    //instantiation
    Fn(expr); // Here T is deduced from the type of expr.
    
    For auto type variable, we define a variable like-

    auto var = expr; // Here type of var is deduced from the type of expr

    Similar to function template constant-ness and reference-ness also have same effect as shown below-
    Code:
    int i = 10;
    const int ci = 10;
    int& ri = i;
    const int& cri = i;
    
    auto var = ci; // Type of var is deduced to be int
    auto var = ri; // Type of var is deduced to be int
    auto var = cri; // Type of var is deduced to be int
    
    auto& var = ci; // Type of var is deduced to be const int&
    auto& var = ri; // Type of var is deduced to be int&
    auto& var = cri; // Type of var is deduced to be const int&
    
    auto* var = &ci; // Type of var is deduced to be const int*
    auto* var = &ri; // Type of var is deduced to be int*
    auto* var = &cri; // Type of var is deduced to be const int*
    
    C++14 adds more usage of auto where you can deduce the return type of a function from its return expression.

    If you declare a function like -
    Code:
    auto Fn()
    {
        ………
        return expr; 
    }
    
    The return type will be deduced by the compiler from the type of expr

    auto variable is handy while dealing with, collection iterator, range based for loops, function pointer initialization, lamda function return type. Folowing example code will elaborate the usage -

    Code:
    // usage of auto variable for iterator and range based for loop
        std::vector<int> vi = { 1, 2, 3, 4,5};
        for (auto itr = vi.begin(); itr != vi.end(); itr++)
        {
            *itr = *itr * 10;
        }
        cout << "Elements of the vector are : "<<endl;
        for (auto item : vi)
        {
             cout << item << " ";
        }
        cout << endl;
    // Usage of auto for initializing a function pointer with a lamda function. 
        auto f1 = [](int x, int y) { return x + y; }; 
    //Here the type of f1 is deduced to int (*)(int, int). Also the return type of the lamada is deduced from the type of the //expression (x+y)
    
    You can also declare a variable type with the use of decltype keyword -

    decltype(expr) var; // Here the type of var will be exact type of the expr;
    auto and keyword helps programmer from typing long, complex type name which are obvious. Take the following example -
    Code:
        vector<int> V;
        for (int i=0; i < 5;i++)
        V.push_back(10*i);
        vector<int>::iterator it = V.begin();
        while (it!=V.end())
        {
            cout << *it++<<endl;
        }
    
    Here the declaration of the iterator 'it' becomes easier when auto is used like -
    Code:
        auto it = V.begin();
    
    You may also declare and initialize the iterator using decltype as shown below though it does not improve the code.
    Code:
        decltype(V.begin()) it;
        it = V.begin();
    
    decltype keyword is used when declaraing the type of a variable is cumbersome. One difference between auto and decltype is that auto requires initialization but decltype does not need initialization.
    Another difference is that auto deduce the type from the initialization expression but decltype identifies the exact type of the argument expression. Type deduction by auto and type identified by decltype for the same expression may not be same. For example:
    Code:
    int& MyFn(int n);
    
    auto autovar = MyFn(20);
    decltype(MyFn(10)) decltypevar = MyFn(30);   
    
    The type of autovar is int but the type of decltypevar is int&.

    Change a type at one point in the source code and it adapts through type deduction at other locations.

    With the increasing popularity of generic programming the types are left for the compiler to decide and programmer's freedom to avoid typing type names is increasing. Type deduction has become the essential part of modern programming and experienced programmer will see the benifit immediately once they get used to it.
     

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