Sunday, February 26, 2012

Default Template Arguments

Hello everybody, sorry for the delay. I have been so busy although I posted some examples.

Today, I'll show you an useful tool with templates. You will able to define default templates, this, it is so interested especially for containers. 


Some chapters before we defined a Queue class. Now, we retake it.


How it looks a Default Template:

template<typename T, typename CONT = std::vector<T> >


T, like previous examples, is the type that contain our class. And CONT is the template parameter that the class will use as container.

For example:

Using the new class Queue ( at the end you the sheet or here ):

Queue<int> queueInt;
Queue<int, std::deque<int> > queueIntWithDeque;

So, we can indicate the container that our queue will use or not. Taking the dafault argument in this case.

Even, we can define a default type, for example:

template<typename T = doubletypename CONT = std::vector<T> >

And now, we can use the default templates, for instance:

Queue<> defaultExample;


Another example which I will explain deeper in the next chapter are the nontype template parameters.
Basically, we are also able to define these as default.

A short example:

template<int H = 2int W = 2  > (example of nontype template parameters here)

Okey, let's see the new class Queue:


#include <iostream>
#include <stdexcept>
#include <vector>
#include <deque>

template <typename T = double, typename CONT = std::vector<T> >
class Queue{
      private:
          CONT elements;
          
      public:
          Queue(){}//default constructor
          T back(){ // return last element
              return elements.back();
          }
          void push(const T& element){//add an element
               elements.push_back(element);
          }
          void pop(){//erase first element
              if (elements.empty()) {
                  throw std::out_of_range("Stack<>::pop(): empty stack");
              }
              elements.erase( elements.begin() );
          }
          bool empty(){return elements.empty();}
          int size(){return elements.size();
          }
          T front(){
              if (elements.empty()) {
                  throw std::out_of_range("Stack<>::pop(): empty stack");
              }
              return elements.front();}
};
          
template< typename T>
void iterate_Queue(Queue<T>& queue){
      while(not queue.empty()) {
          std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
          std::cout << "with size:"<< queue.size() << std::endl;
          queue.pop();
      }
      std::cout << std::endl;
}
// overloading function
template< typename T, typename CONT>
void iterate_Queue(Queue<T,CONT>& queue){
      while(not queue.empty()) {
          std::cout << "(" << queue.front()<< ", " << queue.back() << ")";
          std::cout << "with size:"<< queue.size() << std::endl;
          queue.pop();
      }
      std::cout << std::endl;
}


typedef Queue<int> intQueue;// It's common to define typedef's in order to simply 

int main() {
      try{
          intQueue y;
          y.push(1);
          y.push(2);
          y.push(3);
          y.push(4);
          iterate_Queue(y);

          y.push(15);
          iterate_Queue(y);

          
          Queue<int, std::deque<int> > newExample;
          newExample.push(10);
          newExample.push(20);
          newExample.push(30);
          newExample.push(40);
          iterate_Queue(newExample);
          newExample.push(150);
          iterate_Queue(newExample);
          std::cout << "complet dafault example " << std::endl;
          Queue<> defaultExample;
          defaultExample.push(1.3);
          defaultExample.push(2.2);
          defaultExample.push(3.1);
          defaultExample.push(4.4);
          iterate_Queue(defaultExample);
          defaultExample.push(150);
          iterate_Queue(defaultExample);
          
      } catch (const std::exception& ex) {
          std::cerr << "Exception: " << ex.what() << std::endl;
      } 
      
      return 0;
}

output:



(1, 4)with size:4
(2, 4)with size:3
(3, 4)with size:2
(4, 4)with size:1


(15, 15)with size:1


(10, 40)with size:4
(20, 40)with size:3
(30, 40)with size:2
(40, 40)with size:1


(150, 150)with size:1


complet dafault example 
(1.3, 4.4)with size:4
(2.2, 4.4)with size:3
(3.1, 4.4)with size:2
(4.4, 4.4)with size:1


(150, 150)with size:1




RUN SUCCESSFUL (total time: 615ms)


However, default templates have their own shortcomings, for example, in this case, we did the Queue methods using functions like: back(), front(), ...

All this methods has to be implemented in the container we are going to use. In the case we didn't, we'll get some errors in time compiling (fortunately).

Templates are so useful for this, you can check errors in compiling time. If you saw the example of a Matrix class that I did, you can see this feature. We can multiply matrixes, and at compiling time, we can be sure all the products are possible.

Enjoy it!

No comments:

Post a Comment