相互需要的前向声明模板

时间:2018-03-12 19:13:04

标签: c++ templates generics forward-declaration

uni的一位教授希望我们使用std::vector来实现堆栈,并编写一个"取消堆栈"它的迭代器(也就是迭代器,当迭代时弹出堆栈的顶部) 一切都可以没事,直到他还决定他希望所有这些都是通用的,使用模板和所有。那是什么时候开始的。

所以我做的第一件事就是写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。所以我尝试了其他的东西:我将VectorStackVectorStackIterator的声明放在同一个文件的开头,而只放,我把定义所有的方法。这是它的样子:

//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;
};

所有这些都是上面声明的每个方法的定义。我不会想到错误所在,但请问您是否要我提供。

这是我对解决方案的最接近的尝试,但是当我在Incomplete types not allowed here函数中声明VectorStack<int>对象时,编译器仍抱怨main

#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循环的标题行上出现错误。登记/> 看起来编译器不会超越前向声明,而是实际的定义使一切都完整。

我的选项用完了,你有什么想法吗?

1 个答案:

答案 0 :(得分:1)

关注你的帖子有点难。但总的来说,有一些事情需要牢记:

  • 按值存储的类成员要求在声明包含类时提供完整类型,因为编译器需要知道对象应该占用多少内存。
  • 在声明包含类时,指针和引用成员不需要,因为大小总是指针的大小。
  • 一旦开始使用相关对象,就总是需要完整类型,因为编译器需要知道该类型应包含哪些成员变量和函数。
  • 如果您遇到无法解决“不完整类型”错误的情况,请仔细检查您的设计以确保其有意义;你不希望(例如)两种类型循环包含彼此(按值,再次引用和指针都很好)。

那就是说,我认为处理这个问题的标准方法是:

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

由于您的两个类都是模板化的,因此需要将实现放在头文件中,因此您可能需要小心构建文件的方式,以强制执行这些部分的顺序。