4

我有一个这样定义的模板类:

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

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

这段代码可以证明这种方法的一个问题:

#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还没有定义,只是声明了。Parent如果我将and的声明顺序更改为Child

#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没有完全定义。

有什么办法可以解决这个问题并仍然使用模板吗?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 的不同调用:

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

不太精明的人会说“你为什么不简单地添加std::list<Instance *>作为成员Modelstd::list<GuiTextElement *>作为成员GuiRenderer?”。这里有几个原因:

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

1 回答 1

1

我认为您向我们展示的代码没有表现出您所询问的问题。

我做了最小的更改以克服早期的编译器错误,因此我可以达到可以重现您的问题的地步。但是,这样做了,你的问题就不存在了。

这是我的更改:

  • 添加#include <list>.
  • 将每个实例替换iteratortypename Container::iterator
  • Container从公共而不是私人继承,list所以我可以访问它的iterator类型。

您可以尝试将它们一一添加,以查看每个解决了哪些错误-它们都与您的问题无关。或者您可以下载代码并自行编译。

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

另外,我看不到引用的行如何出现您描述的错误。T是否不完整并不重要C,因为您所做的只是:

  • static_castaT *到 a Proxy *,只需要T声明,而不是定义。
  • 访问 a 的成员Proxy,它不依赖于 type T
  • 将 a 设置C *为 NULL,只需要C声明,不需要定义。

上一行取决于调用an ,这显然是您自定义类,可能依赖于或以某种方式,但这不可能导致下一行出现错误。operator *iteratorTC

所以,有几种可能:

  • 该错误不是您描述的错误,也不是您描述的行,问题出在iterator类或list您没有向我们展示的类中。
  • 您向我们展示的代码与您的真实代码如此不同,以至于它甚至不再有导致问题的部分的踪迹。
  • 我的微不足道的变化并不像我想象的那么微不足道,并且以某种方式解决了您的问题。
于 2013-06-13T20:47:37.600 回答