Friday, 23 August 2013

Tricky Stuff

Keyword typename:

template <typename T> class MyClass {
typename T::SubType * ptr;
...
};


Now keyword typename is very important in the declaration above and is required otherwise compiler will treat T::SubType * ptr as multiplication of static member SubType inside T with ptr; T being generic type , it is programmer's job to tell compiler that SubType is a type inside T. 
so with typename the declaration means ptr is a pointer of SuyType.


Template template parameters

template t<typename T, typename Container>
class Stack {...}; //declaration

Stack<int, std::vector<int> > vStack; //stack of int using vector as container.

can this be written as

Stack<int, std::vector> > vStack; 

To do this template declaration changes as follows. Following applies to class templates only.

template <typename T, template< typename ELEM> class CONT = std::deque>
class Stack { ... };

Now in the code above second template parameter is a class template

template < typename ELEM > class CONT //and is defaulted to std::deque.

which effectively declare container as std::deque<ELEM> but what we want is std::deque<T>

so ELEM is not used hence we can ignore the type and modify the declaration as follows, that way same type T is used for std::deque.

template < typename T, template <typename> class CONT = std::deque>
class Stack{ ... };

//member functions change accordingly

template <typename T, template< typename> class CONT>
Stack<T,CONT>::push(const T& elem)
{
       elems.push_back(elem); 
}


'typename' or 'class'

one would argue that above declaration can be written something like

template <typename T, template<typename> typename CONT = std::deque>
class Stack {...}; //ERROR

//previous declaration:

template <typename T, template<typename> class CONT = std::deque>
class Stack {...}; //CORRECT


we replaced the word class with typename with class and that is an error because, this word class indicates that it is a class template or second template param of Stack is a class template. i.e. std::deque or any container for that matter is a class template hence the key word class is used as in a C++ class.

Initialising member types of type T ( generic type)

To make sure that a member of a class template, for which the type is parameterized, gets initialized, you have to define a default constructor that uses an initializer list to initialize the member:
template <typename T> class MyClass {
private: T x;
public:
MyClass() : x() {
// ensures that x is initialized even for built-in types }
...

}; 

Using string literals in as template params


// note: reference parameters

template <typename T>
inline T const& max (T const& a, T const& b) {

return a < b ? b : a; }
int main() 
{
  std::string s;
  ::max("apple","peach");    //OK same type
  ::max("apple","tomato");
// ERROR: different types ::max("apple",s); 

                                         // ERROR:       different types


here type of "apple" is char const[6] and that of tomato is char const[7] hence they are different type. solution is to call them using 
::max(std::string("apple"), std::string("tomato")); // now types are same.
or alternatively max can be defined without using references as follows

// note: nonreference parameters
template <typename T> inline T max (T a, T b)
{

        return a < b ? b : a; 
}
int main() 
{
     std::string s;
     ::max("apple","peach"); // OK: same type 
     ::max("apple","tomato"); // OK: to same type 
     ::max("apple",s); // ERROR: different types
}

For more information refer the book.

















Tuesday, 13 August 2013

Non-type template parameters

Generally in class as well as function templates the parameters are of generic type T. But they don't have to be. The params can be non-type params or type of the param can be fixed. e.g.

template <typename T, int MAX>
class Stack 
{
public:
         void push(const T&);
         ...
private:
          T [MAX];
         ... 
}; //stack with fixed number of elements.

template<typename T, int MAX>
void Stack<T,MAX>::push(const T&) {...} //function definition. 

In function definition outside the class, both the params should be specified.

//usage

main()
{
...
Stack<int, 20> intStack20;
Stack<std::string, 40> stringStack40;
...
}


Default values:

Default values can also be specified for non type template params e.g.

template <typename = int, int MAX =100>
class Stack { ... } 

//use 
main()
{
       Stack mystack; //stack of 100 ints.
}

But this is not a good idea from generic programming point of view, this way you are restricting the generalisation and this warrants documentation of default params.

Default values for function templates (Functors):

template <typename T, int NUM=1>
T addNUM(const T& val)
{
      return val + NUM;
}

//use

main()
{
...
//assume vec is a vector of ints

//adds 20 to every element in vec but results are lost as they are not stored in //vec - use transform instead main point is to demo default values in function templates for non type template params.

for_each( vec.begin(), vec.end(), addNUM<int,20>);
...
//assume dvec is a vector of doubles
...
//adds 1 (by default) to every element of dvec.
for_each(dvec.begin(), dvec.end(), addNUM<double>);
...

}

Restrictions on non type template parameters.

  • They may be const integral values including pointers to objects with external linkage.
  • floating points numbers and class type objects are not allowed.
//ERROR: double not allowed
template <double TEST> double calc(double v) { return v*TEST;}

//ERROR: class type objects not allowed.
template <std::string str> class Myclass {...};

template <typename const char* name>
class MyName{...};

MyName<"Mandar"> x; //ERROR: string literal is not allowed.

const char* s="Mandar";
MyName<s> x; //ERROR: s is a pointer to object with internal linkage.

extern const char s[] ="Mandar";
MyName<s> x; //OK: global array with external linkage.

Monday, 5 August 2013

Class Templates


Class templates

  • Class Templates are same as function templates, but they are classes.
template <typaname T>
class Stack {

public:
        void push(const T& ref);
        T pop();
        T top(); 
        bool empty() {return elems.empty();} //inline
private:
        std::vector<T> elems;

};

T Stack<T>::pop() {
       if( elmems.empty())
       {
             throw std::out_of_range("pop: stack is empty");
       }
       T elem = elems.back(); //get the last element of vector
       elems.pop_back();        //remove the last element
       
       return elem;
}

  • code above is template delcaration for any type T
  • The member functions are declared, some of them defined inline.
  • Other are defined outside as 'function templates'
  • When templates is instantiated like Stack<int> intStack; then only class instance is created.
  • Instances of function are created only when the functions are called such as intStack.push(7); This way only push will be instantiated and not pop()
  • Thus C++ gives no overhead, you pay for what you use.
  • That way we can instantiate class templates for the types that do not support all the template member functions.
  • Any type is allowed in while instantiating templates as long as operations are supported.
  • Stack<Stack(int)> > intStackStack; // space> > is not required since C++11

Full specialization:

template <>
class Stack<std::string> {

public:
        void push(const std::string& ref);
        T pop();
        T top(); 
        bool empty() {return elems.empty();} //inline
private:
        std::vector<std::string> elems;

};
void Stack<std::string>
push(const std::string& ref) {
       elems.push_back(ref);
}
  • To specialize the class template use template<> syntax.
  • If class template is specialized then ALL the member function must be specialized by writing corresponding ordinary functions as shown above (push) for that type.
  • If that is not desirable then instead of specializing the class template individual member function can be specialized without specializing the class  itself.
  • Specialization can differ from the original implementation of template function or class template. ( that is why this called so! )
  • This specialization is called as full specialisation, i.e. all the types are known.

Partial specialization:

template <typename T1, typename T2>
class Part {...}  //original def.

template <typename T>
class Part<T,T> {...}

template <typename T1, typename T2>
class Part <T1*, T2*> {...}

template <typename T1, int>
class Part < T1,int> {...}
  • This is partial specialization at least one of the type is generic.
//use
Part<int, float> p1; //matches Part<T1,T2>
Part<float, int> p2; //matches Part<T1, int>
Part<float,float> p3; //matches Part<T,T>
Part<float*, float*> p4; //Error ambiguous matches Part<T1*,T2*> & Part<T1,T2> 
  • Partial specialization is allowed only for class templates.
  • Function templates must be fully specialized and cannot be partially specialized.

Default template arguments:

template <typename T, typename CONT = std::vector<T> >
class Stack {
public:
        void push(const T&);
        T pop();
        T top();
        bool empty() {return elems.empty();}
private:
        CONT elems; //generic container defaulted to std::vector.
};

template<typename T, typename CONT>
void Stack<T,CONT>::push(const T& ref) {
       elems.push_back(ref);
}
  • Thus, here default template argument is specified and that way if second argument is ignored then std::vector is used as container or you can change the container as per requirements. e.g.
//use
//stack of int implemented using default std::vector
Stack<int> intStack; 


Stack<double, std::deque<double> > dblStack;
//stack of doubles with std::deque<> as a container.






Thursday, 1 August 2013

Introduction to C++ templates ( Function Templates)


//max.h

template<typename T>inline T const& max(T const& a, T const& b){        return a > b ? a : b;}

  • T is template paramter. ( There can be more than one template params)
  • a and b are call parameters.
  • Look at return type it is const T& , function returns the max of two by ref to const. 
  • It is evil to return values by ref. or pointer if they are local to function. 
  • But it is ok to return temporaries by reference to const only. (the scope is extended outside the function and return value optimisation kicks in.
  • Returning by ref to non-const is not allowed as ref to non-const are bound to lvalues and temporaries are not lvalues they are nameless objects.
  • Template function can be used as follows.
//max.cpp
std::cout << max(2,3);
int a = 20; ::max(10,a)
std::string s1 = "Mandar" , s2 ="MandarVaidya";::max(s1,s2);
std::complex<float> c1, c2;...::max(c1, c2) ;  // (1) error as > operator is not supported.
  • max is qualified with :: so that correct max is called as there is std::max(), we don't want to call that function.
  • Templates are compiled twice, one without instantiation to check for syntax
  • Second at the time of instantiation to check if all the calls are valid e.g. unsupported calls as reported as "compile time" errors. such as (1)
  • For each type new template function is instantiated as shown about.
  • max(2,3); // arguments deduced as int
  • max(2.0,2); //error both args should be of same type
  • max<double>(2,5); //OK
  • max(static_cast<double>(2),5.3);  //OK

Variation - 1

  • Look at return type it is no longer ref. it is an object of type T1
  • During return if T2 object is converted to T1 (depending on result of comparison) and for that local temporary is created and it cannot be returned by reference. Hence return is done by value. ( copy ctor will be called )
  • max(42, 66.66); will return 66 as T1 is int here.
  • max(66.66, 42); will return 66.66 as T1 is double.
  • drawbacks return type is fixed and order of arguments varies the result.

Variation -2 

  • Return type is specified as double and other two types are deduced form the arguments.
const double& max (double const&, double const&);
const std::string& max (std::string const&, std::string const&); 

template <typename T1, typename T2>
inline T1 max(const T1& a, const T2& b)
{
         return a > b ? a: b;
}


template<typename T1, typename T2, typename ReturnType)
inline ReturnType max(const T1& a, const T2& b);

max<int, double, double>(2,3.3); //OK 
  • Tedious way to specify return type.

simplification

template<typename ReturnType, typename T1, typename T2>
inline ReturnType max(const T1& a, const T2& b);

max<double>(10,20); //OK 

  • Return type is specified as double types T1 and T2 are omitted but will be deduced form the called arguments.