operator<< for Class Templates

operator<< for Class Templates

Let’s implement operator<< for the Queue class template.

As before, the operator<< needs to be a non-member function, but it’s complicated by the need to access private parts of the template class, and the need to use TYPE.

So, we add friend functions to the Queue template class:

//
//  Queue.h
//  Queue Template Class with friends and Nesting
//
//  Created by Bryan Higgs on 10/17/24.
//

#ifndef Queue_h
#define Queue_h

// The Queue
template <typename TYPE>
class Queue
{
  friend 
  std::ostream &operator<<(std::ostream &os,
                           const Queue<TYPE> &q)
  {
    os << "< ";
    for (Queue<TYPE>::QueueItem *qi = q.m_front;
         qi != 0; qi = qi->getNext())
    {
      os << *qi << ' ';    // Use QueueItem's operator<<
    }
    os << ">";
    return os;
  }
  
public:
  typedef void func_t(TYPE value); // Note change of signature
  
  Queue()
    : m_front(0), m_back(0)
  {}
  
  ~Queue()
  {
    while (!IsEmpty())
      RemoveFromFront();
  }
  
  void AddToBack(const TYPE &value)
  {
    QueueItem *qiPtr
      = new QueueItem(value); // Note removal of <TYPE>
    if (IsEmpty())
      m_front = m_back = qiPtr;
    else
    {
      m_back->setNext(qiPtr);
      m_back = qiPtr;
    }
  }

  const TYPE RemoveFromFront()
  {
    if(IsEmpty())
      return 0;  // Nothing to remove

    QueueItem *qiPtr = m_front; // Note removal of <TYPE>
    m_front = m_front->getNext();
    const TYPE theValue = qiPtr->getValue();
    delete qiPtr;  // Deallocate the QueueItem
    return theValue;
  }

  bool IsEmpty() const
  {
    return m_front == 0;
  }
  
  void ForEachItem(func_t *func)
  {
    for (QueueItem *qi = m_front; // Note removal of <TYPE>
        qi != 0; qi = qi->getNext())
    {
      func(qi->getValue());
    }
  }

private:
  // A Queue item, now private
  class QueueItem // Note removal of <TYPE>
  {
    friend
    std::ostream &operator<<(std::ostream &os,
                             const QueueItem &qi)
    {
      os << qi.getValue();
      return os;
    }

  public:
    QueueItem(const TYPE &item)
      : m_item(item), m_next(0)
    {}

    const TYPE getValue() const
    { return m_item; }
    QueueItem* getNext()
    { return m_next; }
    void setNext(QueueItem* next)
    { m_next = next; }
    
  private:
    const TYPE  m_item;
    QueueItem  *m_next;
  };

  QueueItem *m_front; // Note removal of <TYPE>
  QueueItem *m_back;  // Note removal of <TYPE>
};

#endif /* Queue_h */

… and some small changes to the main program:

//
//  main.cpp
//  Queue Template Class with friends and Nesting
//
//  Created by Bryan Higgs on 10/17/24.
//

#include <iostream>
#include "Queue.h"

void displayItem( int value )
{
  std::cout << ' ' << value;
}

int main(int argc, const char * argv[])
{
  Queue<int> queue;

  for (int i = 0; i < 5; i++)
  {
    queue.AddToBack(i*i);
            // The square of i
  }
  
  queue.ForEachItem(displayItem);
  std::cout << std::endl;
  
  std::cout << queue << std::endl; // Use operator<< for Queue
  
  for (int i = 0; i < 5; i++)
  {
    const int value
          = queue.RemoveFromFront();
    std::cout << "[" << i << "] : "
              << value << std::endl;
  }
  
  return 0;
}

which outputs:

 0 1 4 9 16
< 0 1 4 9 16 >
[0] : 0
[1] : 1
[2] : 4
[3] : 9
[4] : 16
Program ended with exit code: 0