有没有办法解决模板类中的前向声明?

时间:2013-06-12 00:07:26

标签: c++ templates containers

我有一个模板类定义如下:

template <typename T> void ProxyNoOp(T *) {}
template <typename T> void ProxyDelete(T * ptrT) {delete ptrT;}

template <typename T, typename C, void (* Release)(T *) = ProxyNoOp<T>  >
class Proxy
{
public:
    class Container : public std::list<T *>
    {
    public:
        Container() {}
        ~Container() {clear();}

        void clear()
        {
            iterator clsEnd = end();
            for (iterator clsCurrent = begin(); clsCurrent != clsEnd; ++clsCurrent)
            {
                T * ptrT = *clsCurrent;
                static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL;
                Release(ptrT);
            }   
        }
    };

private:
    iterator m_clsPosition;
    C *      m_ptrContainer;

public:
    Proxy() : m_ptrContainer(NULL) {}
    ~Proxy()
    {
        if ( m_ptrContainer != NULL )
        {
            Container * ptrContainer = static_cast<Container *>(m_ptrContainer);
            static_cast<std::list<T *> >(ptrContainer)->erase(m_clsPosition);
        }
    }

    C * GetContainer() const {return m_ptrContainer;}
};

为简洁起见,已删除或更改了许多代码。数据结构的概念是,容器不仅维护对包含元素的引用,而且包含的元素保持对容器的引用PLUS它们在容器中的位置,使得快速删除(恒定时间)和自动(在析构函数中调用) )

此代码可以证明此方法存在问题:

#include "proxy.h" // the above template class

class Child;
class Parent : public Proxy<Child, Parent>::Container {};
class Child : public Proxy<Child, Parent> {};

编译Parent时,Proxy<Child, Parent>::Container的定义会导致此行出错:static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL; - 在编译中,Child尚未定义,仅声明。如果我将ParentChild的声明顺序更改为:

#include "proxy.h"

class Parent;
class Child : public Proxy<Child, Parent> {};
class Parent : public Proxy<Child, Parent>::Container {};

Proxy<Child, Parent>的定义导致此行出错:Container * ptrContainer = static_cast<Container *>(m_ptrContainer);,原因与之前相同 - 在编译中,Parent未完全定义。< / p>

有什么方法可以解决这个问题,仍然使用模板吗? STL,BOOST等中是否有标准数据结构可以实现这一目标?

修改

代码不会按原样编译,我应该更加清楚(从上面引用自己:为了简洁起见,许多代码已被删除或更改。) 。它实际上并不使用std::list<T *>,而是一个实际上是双向链接的,它实际上是相同的。我只是在询问这个问题时不想发布大量的辅助代码。

我并没有要求对数据结构本身进行批评(虽然我很好听)但是对于那些想知道为什么在这里创建它的人来说是一些例子:

游戏引擎具有这些模型的模型和实例,例如战争游戏,其中有一个坦克模型和从中创建的任意数量的坦克。这些课程可以这样写:

class Instance;
class Model : public Proxy<Model, Instance, ProxyDelete<Instance> >::Container {...};
class Instance : public Proxy<Model, Instance, ProxyDelete<Instance> > {...};

当删除模型时,也应该删除从该模型创建的所有实例(因此ProxyDelete<Instance>模板参数),因为没有模型的实例存在实例是没有意义的。

假设您的图形用户界面(GUI)渲染器跟踪所有可见元素(按钮,框架等),因此它知道要渲染的内容,但文本的呈现方式不同,因为它是对图形API的不同调用:< / p>

class GuiTextElement;
class GuiRenderer : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> >::Container {...};
class GuiTextElement : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> > {...};

不那么敏锐的人会说“你为什么不简单地将std::list<Instance *>作为Model的成员添加std::list<GuiTextElement *>作为GuiRenderer的成员?”。原因如下:

  1. 当容器被销毁时,它们的容器“拥有”的元素不会被自动删除(是的,我知道boost::ptr_list)。
  2. 如果不添加其他类成员,则无法使用包含的元素引用容器。例如,要从Model获得Instance
  3. 如果您确实添加了一个类成员来完成#2,那么当从容器中删除元素时,您还需要更新它。
  4. 对于速度狂热者 - 如果包含的元素不保持迭代器到它们在容器中的位置,则在删除该元素之前,必须首先搜索容器。

1 个答案:

答案 0 :(得分:1)

我不认为您向我们展示的代码会显示您所询问的问题。

我做了一些最小的修改以克服早期的编译器错误,所以我可以达到可以重现你的问题的程度。但是,这样做之后,你的问题就不存在了。

以下是我的更改:

  • 添加#include <list>
  • iterator的每个实例替换为typename Container::iterator
  • Container公开而非私下继承list,因此我可以访问其iterator类型。

您可以尝试逐个添加它们,以查看每个解决的错误 - 它们都与您的问题无关。或者您可以download the code并自行编译。

我用三个不同的编译器编译了这个,clang-4.2,g ++ - 4.6和g ++ - 4.2-apple;在适当的情况下,在C ++ 11和C ++ 98模式下;启用-Wall。在所有情况下,它编译时没有任何错误或警告。

此外,我看不到引用的行如何产生您描述的错误。 TC是否不完整无关紧要,因为您所做的只是:

  • static_cast T *Proxy *,只需要声明T,未定义。
  • 访问Proxy的成员,该成员不依赖于T类型。
  • C *设置为NULL,只需要声明C,未定义。

上一个行取决于在operator *上调用iterator,而这显然是您的自定义类,可能依赖于T或{{ 1}}在某种程度上,但这不可能在下一行引起错误。

所以,有几种可能性:

  • 错误不是您描述的错误,也不是您描述的错误,问题出在您未向我们展示的C班级或iterator班级。
  • 您向我们展示的代码与您的实际代码完全不同,因此它甚至不再有导致问题的部分痕迹。
  • 我的琐碎变化并不像我想象的那样微不足道,并以某种方式解决了你的问题。