简体   繁体   English

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

[英]Any way to resolve forward declaration in a template class?

I have a template class defined as such: 我有一个模板类定义如下:

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

A lot of the code has been removed or changed for the sake of brevity. 为简洁起见,已删除或更改了许多代码。 The idea of the data structure is that not only does a container maintain references to contained elements, but the contained elements maintain a reference to the container PLUS their position in the container, making removal quick (constant time) and automatic (called in the destructor) 数据结构的概念是,容器不仅维护对包含元素的引用,而且包含的元素保持对容器的引用PLUS它们在容器中的位置,使得快速删除(恒定时间)和自动(在析构函数中调用) )

A problem with this approach can be demonstrated by this code: 此代码可以证明此方法存在问题:

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

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

When compiling Parent , the definition of Proxy<Child, Parent>::Container causes an error at this line: static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL; 在编译ParentProxy<Child, Parent>::Container的定义会在此行导致错误: static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL; - at the point in the compilation, Child has not been defined, only declared. - 在汇编中, Child尚未定义,仅被声明。 If I change the declaration order of Parent and Child to be this: 如果我将ParentChild的声明顺序更改为:

#include "proxy.h"

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

the definition of Proxy<Child, Parent> causes an error at this line: Container * ptrContainer = static_cast<Container *>(m_ptrContainer); Proxy<Child, Parent>的定义导致此行出错: Container * ptrContainer = static_cast<Container *>(m_ptrContainer); , the reason being the same as before - at that point in the compilation, Parent is not fully defined. ,原因与之前相同 - 在编译中的那一点, Parent没有完全定义。

Is there any way I can get around this and still use templates? 有什么方法可以解决这个问题,仍然使用模板吗? Is there a standard data structure in STL, BOOST, etc. that accomplishes this? STL,BOOST等中是否有标准数据结构来实现这一目标?

EDIT: 编辑:

The code will not compile as-is, I should have been clearer about that (to quote myself from above: A lot of the code has been removed or changed for the sake of brevity. ). 代码将不会按原样编译,我应该更清楚(从上面引用自己: 为了简洁起见,已经删除或更改了许多代码。 )。 It doesn't actually use std::list<T *> , but a custom doubly linked that is effectively the same. 它实际上并没有使用std::list<T *> ,而是一个实际上相同的自定义双向链接。 I didn't want to post a ton of ancillary code when only asking about this one issue. 当我只询问这个问题时,我不想发布大量的辅助代码。

I wasn't asking for critique on the data structure itself (although I am fine to hear it), but for those wondering why it was created here are a couple examples: 我并没有要求对数据结构本身进行批评(尽管我听得很清楚),但对于那些想知道为什么在这里创建的人来说,有几个例子:

A game engine has models and instances of those models, such as a war game where there is one tank model and any number of tanks created from it. 游戏引擎具有这些模型的模型和实例,例如战争游戏,其中有一个坦克模型和由其创建的任意数量的坦克。 The classes could be written as such: 这些类可以这样编写:

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

When a model is deleted, all instances made from that model should also be deleted (hence the ProxyDelete<Instance> template parameter) since it doesn't make sense for an instance to exist without the model it was made from. 删除模型时,也应删除从该模型创建的所有实例(因此, ProxyDelete<Instance>模板参数),因为没有模型的实例存在实例是没有意义的。

Suppose your graphical user interface (GUI) renderer keeps track of all visible elements (buttons, frames, etc.) so it knows what to render, but text is rendered differently since it is a different call to the graphics API: 假设您的图形用户界面(GUI)渲染器跟踪所有可见元素(按钮,框架等),因此它知道要渲染的内容,但文本的呈现方式不同,因为它是对图形API的不同调用:

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

The less astute will say "why don't you simply add std::list<Instance *> as a member of the Model and std::list<GuiTextElement *> as a member of GuiRenderer ?". 精明的人会说“为什么不简单地将std::list<Instance *>Model的成员,将std::list<GuiTextElement *>GuiRenderer的成员?”。 Here are a few reasons: 原因如下:

  1. Elements that are "owned" by their containers are not automatically deleted when the container is destroyed (yes, I am aware of boost::ptr_list ). 当容器被销毁时,它们的容器“拥有”的元素不会被自动删除(是的,我知道boost::ptr_list )。
  2. There is no way, without adding another class member, to refer to the container using a contained element. 如果不添加其他类成员,则无法使用包含的元素引用容器。 For example, to get a Model from an Instance . 例如,从Instance获取Model
  3. If you did add a class member to accomplish #2, you would also need to update it when elements are removed from their containers. 如果您确实添加了一个类成员来完成#2,那么当从容器中删除元素时,您还需要更新它。
  4. For the speed fanatics - if contained elements don't maintain an iterator to their position in the container, before that element can be removed the container must first be searched for it. 对于速度狂热者 - 如果包含的元素不保持迭代器到它们在容器中的位置,则在删除该元素之前必须首先搜索容器。

I don't think the code you've shown us exhibits the problem you're asking about. 我不认为你向我们展示的代码表明了你所问的问题。

I made minimal changes to get past the earlier compiler errors, so I could get to the point where I could reproduce your problem. 我做了一些最小的修改以克服早期的编译器错误,所以我可以达到可以重现你的问题的程度。 But, having done so, your problem does not exist. 但是,这样做,你的问题就不存在了。

Here are my changes: 以下是我的更改:

  • Add #include <list> . 添加#include <list>
  • Replace each instance of iterator with typename Container::iterator typename Container::iterator替换iterator每个实例
  • Make Container inherit from list publicly instead of privately, so I get access to its iterator type. 使Containerlist公开而不是私有地继承,因此我可以访问其iterator类型。

You can try adding them one by one to see what errors each one solves—none of them have anything to do with your problem. 您可以尝试逐个添加它们以查看每个解决的错误 - 它们都与您的问题无关。 Or you can download the code and compile it yourself. 或者您可以下载代码并自行编译。

I compiled this with a three different compilers, clang-4.2, g++-4.6, and g++-4.2-apple; 我用三个不同的编译器编译了这个,clang-4.2,g ++ - 4.6和g ++ - 4.2-apple; in both C++11 and C++98 modes where appropriate; 在适当的情况下,在C ++ 11和C ++ 98模式下; with -Wall enabled. 启用-Wall。 In all cases, it compiled without any errors or warnings. 在所有情况下,它编译时没有任何错误或警告。

Also I don't see how the referenced line could have the error you described. 另外,我没有看到引用的行如何产生您描述的错误。 It doesn't matter whether T or C are incomplete, because all you do is: TC是否不完整无关紧要,因为您所做的只是:

  • static_cast a T * to a Proxy * , which only requires having T declared, not defined. static_cast一个T *到一个Proxy * ,只需要声明T ,没有定义。
  • Access a member of a Proxy , which does not depend in any way on type T . 访问Proxy的成员,该Proxy不依赖于类型T
  • Set a C * to NULL, which only requires having C declared, not defined. C *设置为NULL,只需要声明C ,而不是定义。

The previous line depends on calling operator * on an iterator , and that's apparently a custom class of your that might be dependent on T or C in some way, but that couldn't possibly cause an error on the next line. 一行依赖于在iterator上调用operator * ,而且显然是你的自定义类可能以某种方式依赖于TC ,但这不可能在下一行引起错误。

So, there are a few possibilities: 所以,有一些可能性:

  • The error is not the error your described, nor is it on the line you described, and the problem is in either the iterator class or the list class that you didn't show us. 错误不是您描述的错误,也不是您描述的错误,问题出在iterator类或您未向我们展示的list类中。
  • The code you've shown us is so different from your real code that it no longer has even a trace of the part that causes a problem. 您向我们展示的代码与您的真实代码有很大的不同,它甚至不再有导致问题的部分痕迹。
  • My trivial changes aren't as trivial as I thought and somehow fixed your problem. 我的琐碎变化并不像我想象的那样微不足道,并以某种方式解决了你的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM