0

I am trying to implement a templated Queue in a separate file from where the class is defined. There are other questions addressing this type of thing and they point to this FAQ (https://isocpp.org/wiki/faq/templates#template-specialization-example) and provide the solution of inserting another definition below the function definition in the file where everything is implemented (see below). It is also useful to note that the FAQ mentions updating for C++11 whereas I am using C++14.

Despite reading through this I am still confused about

1) The syntax to make this work with the constructor/destructor (Edit: the code runs if I put the instantiation line at the bottom of queue.cpp)

2) why you need to define a type in the new declaration and whether or not it affects how you use the class and

3) whether or not I should be using templates like this in the first place. It seems like the language doesn't want me to.

queue.h

#pragma once
#include <string>

namespace tiff {
    class QueueEmpty: public std::exception{
        const char* what() const noexcept override {
            return "Empty Queue. Nothing to return.";
        }
    };

    template<class E>
    class Queue {
    private:
        int elements;
        int capacity;
        int front;
        int rear;
        E* q;
        E* resize();

    public:

        Queue(int);

        ~Queue();
        int size() const;

        bool empty() const;

        const E& get_front() const throw(QueueEmpty);

        void enqueue(const E &e);

        void dequeue() throw(QueueEmpty);

    };

}

queue.cpp

#include "queue.h"

namespace tiff {

    template <typename E>
    Queue<E>::Queue(int size) {
        elements = 0;
        capacity = size;
        front = 0;
        rear = 0;
        q = new E[size];
    }




    template <typename E>
    Queue<E>::~Queue() {
        delete q;
    }

    template <typename E>
    int Queue<E>::size() const {
        return elements;
    }

    template <typename E>
    bool Queue<E>::empty() const{
        return (elements == 0);
    }

    template <typename E>
    const E& Queue<E>::get_front() const throw(QueueEmpty){
        if (empty()){
            throw QueueEmpty();
        }
        return q[front];
    }

    template <typename E>
    void Queue<E>::dequeue()throw(QueueEmpty){
        if(empty()){
            throw QueueEmpty();
        }
        front = (front + 1) % capacity;
        ++elements;
    }

    template <typename E>
    void Queue<E>::enqueue(const E &e) {
        if (elements == capacity){
            resize();
        }
        q[rear] = e;
        rear = (rear + 1) % capacity;
        --elements;
    }

    template <typename E>
    E* Queue<E>::resize() {
        capacity = elements*2;
        E* new_q = new E[capacity];
        for (int i = 0; i < elements; ++i){
            new_q[i] = q[i];
        }
        E* temp = q;
        q = new_q;
        delete temp;
    }

    template class Queue<int>; 
}

main.cpp

#include <iostream>
#include "queue.h"
#include "gtest/gtest.h"

int main(){
    tiff::Queue<int> q = tiff::Queue<int>(5);
    return 0;
}

After reading the FAQ, I am still confused about what is happening under the hood. This whole thing is for learning purposes so I would be glad if anyone can point me to more in-depth info about what is going on here.

  • 2
    You get this error because the definition is in another compilation unit. You can read about it [here](https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl). There are ways to separate the definition using explicit instantiation, but it will only work for the instantiated classes. – Gilles-Philippe Paillé Oct 19 '19 at 21:23
  • 3
    The explicit instantation `template class Queue;` must be placed *after all the definitions*. You placed it before most of them, so they don't get instantiated. – StoryTeller - Unslander Monica Oct 19 '19 at 21:47
  • @StoryTeller That fixes the problem... if I only use ints. Doesn't this defeat the purpose of a template? – Tiffany Montgomery Oct 19 '19 at 21:52
  • 1
    You can't have it both ways. Either everything goes into a header, to be instantiated as needed for any type. *Or* you separate the definitions and expose them for a limited set of types. – StoryTeller - Unslander Monica Oct 19 '19 at 21:55

0 Answers0