A professor at uni wanted us to implement a stack using an std::vector
, and to write an "unstacking" iterator for it (that is, an iterator which when iterated over pops the top of the stack).
Everything could have been fine until he also decided he wanted all of it to be generic, using templates and all. That's when hell began.
So the first thing I did was writing template<typename T> class VectorStack
:
//file VectorStack.hpp
template <typename T>
class VectorStack
{
public:
VectorStack();
virtual size_t size();
virtual bool empty();
virtual void push(T obj);
virtual T pop();
private:
vector<T> _data;
};
Implementation may not be relevant here so I'll skip it. Don't hesitate to ask if you need it.
Then I had to write template<typename T> class VectorStackIterator
...
VectorStack
, it must contain at least a pointer to that instance. So VectorStackIterator
needs to know about VectorStack
, which leads us to a first forward declaration.VectorStack
has begin()
and end()
methods which are supposed to return a VectorStackIterator
. So VectorStack
also needs to know about VectorStackIterator
, which leads us to a second forward declaration.So I wrote my iterator in a separate file:
//file VectorStackIterator.hpp
template<typename T>
class VectorStack; //Forward declaration of the VectorStack
template<typename T>
class VectorStackIterator : public iterator<random_access_iterator_tag, VectorStack<T>>
{
public:
VectorStackIterator(size_t n, VectorStack<T>* instance);
T operator--();
bool operator==(VectorStackIterator other);
bool operator!=(VectorStackIterator other);
private:
VectorStackIterator();
T& operator=() {};
size_t _n;
VectorStack<T>* _instance;
};
...and updated my VectorStack
to look like this:
//file VectorStack.hpp
template<typename T>
class VectorStackIterator; //Forward declaration of the iterator
template <typename T>
class VectorStack
{
public:
//...
VectorStackIterator<T> top();
VectorStackIterator<T> bottom();
//...
};
Again, implementation of the iterator may not be relevant.
At this point, I already had the compiler screaming because I was using incomplete types
everywhere. So I tried out something else: I put the declarations of both the VectorStack
and the VectorStackIterator
at the beginning of the same file, and only then, I put the definitions of all of the methods. Here is what it looks like:
//file VectorStack.hpp
#ifndef VECTOR_STACK_HPP
#define VECTOR_STACK_HPP
#include <vector>
using std::vector;
#include <iterator>
using std::iterator;
#include <exception>
using std::out_of_range;
template <typename T>
class VectorStack;
//still had to forward-declare this because VectorStackIterator uses it in its own declaration.
//Class declaration (VectorStackIterator)
template<typename T>
class VectorStackIterator : public iterator<random_access_iterator_tag, VectorStack<T>>
{
public:
VectorStackIterator(size_t n, VectorStack<T>* instance);
T operator--();
bool operator==(VectorStackIterator other);
bool operator!=(VectorStackIterator other);
private:
VectorStackIterator();
T& operator=() {};
size_t _n;
VectorStack<T>* _instance;
};
//Class declaration (VectorStack)
template <typename T>
class VectorStack
{
public:
VectorStack();
virtual size_t size();
virtual bool empty();
virtual void push(T obj);
virtual T pop();
VectorStackIterator<T> top();
VectorStackIterator<T> bottom();
private:
vector<T> _data;
};
All of this is followed by the definition of every method declared above. I don't think that's where the error lies in, but please ask if you want me to provide it.
This is the closest attempt to a solution that I've come up with, but the compiler still complains about Incomplete types not allowed here
when I declare a VectorStack<int>
object in the main
function:
#include "VectorStack.hpp"
int main(int argc, char** argv)
{
VectorStack<int> v; //Incomplete types not allowed here
v.push(0); //Incomplete types not allowed here
v.push(1); //Incomplete types not allowed here
v.push(2); //Incomplete types not allowed here
for (auto it = v.top(); it != v.bottom();) //Incomplete types not allowed here (x2)
{
cout << it-- << endl;
}
return 0;
}
If I try to forward-declare the iterator instead of the vector stack, then the vector stack is no longer incomplete, but the iterator is, and I get errors on the heading line of the for
loop.
It looks like the compiler won't ever go beyond the forward declaration, to the actual definitions that make everything complete.
I'm running out of options, do you have any ideas?
It's a bit hard to follow your post. But in general, there are some things to keep in mind:
That said, I think the standard way to handle this is:
class ClassB;
class ClassA {
ClassB* or ClassB&
}
class ClassB {
ClassA
}
ClassA::implementations // These two can happen in any order, since both ClassA and ClassB are complete at this point
ClassB::implementations
Since both of your classes are templated, the implementations need to be put in header files, so you may need to be careful with the way you structure your files to enforce the order in which these pieces will happen.