【发布时间】:2018-03-12 19:13:03
【问题描述】:
大学的一位教授希望我们使用 std::vector 实现一个堆栈,并为它编写一个“unstacking”迭代器(即,一个迭代器在迭代时弹出堆栈顶部)。
一切都可以很好,直到他还决定他希望所有这些都是通用的,使用模板和所有。那是地狱开始的时候。
所以我做的第一件事就是写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;
};
实现可能与此处无关,因此我将跳过它。不要犹豫,问你是否需要它。
然后我不得不写template<typename T> class VectorStackIterator...
- 为了让该迭代器解压
VectorStack的实例,它必须至少包含一个指向该实例的指针。所以VectorStackIterator需要了解VectorStack,这导致我们进行第一次前向声明。 - 而且,
VectorStack具有begin()和end()方法,它们应该返回VectorStackIterator。所以VectorStack还需要了解VectorStackIterator,这导致我们进行第二次前向声明。
所以我把我的迭代器写在一个单独的文件中:
//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;
};
...并将我的VectorStack 更新为如下所示:
//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();
//...
};
同样,迭代器的实现可能不相关。
此时,我已经让编译器尖叫了,因为我到处都在使用incomplete types。所以我尝试了其他方法:我将VectorStack 和VectorStackIterator 的声明放在同一个文件的开头,只有这样,我把所有方法的定义.这是它的样子:
//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;
};
所有这些都跟在上面声明的每个方法的定义之后。我不认为这就是错误所在,但请询问您是否希望我提供它。
这是我提出的最接近解决方案的尝试,但是当我在 main 函数中声明 VectorStack<int> 对象时,编译器仍然抱怨 Incomplete types not allowed here:
#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;
}
如果我尝试前向声明迭代器而不是向量堆栈,则向量堆栈不再不完整,但迭代器是不完整的,并且我在 for 循环的标题行出现错误。
看起来编译器永远不会超越前向声明,而是使一切都完成的实际定义。
我的选项不多了,你有什么想法吗?
【问题讨论】:
-
我很难关注你的帖子,我已经失去了
#include "vectorstack.cpp"。为什么要包含源文件?请阅读minimal reproducible example 并尝试使您的示例更简洁 -
搞错了,我的意思是
VectorStack.hpp。 -
仍然是太多的代码。两个类和一个方法应该足以证明问题
-
那么迭代器应该是返回栈顶的值,同时将其从栈中移除?这听起来根本不像一个迭代器,你只需要一个函数。
-
另一方面,这里常用的方法是将
VectorStackIterator类放在VectorStack类中。那么你就不需要转发声明任何东西了。
标签: c++ templates generics forward-declaration