Проблема с указателем на подкласс шаблона - PullRequest
3 голосов
/ 06 июля 2010

В момент безумия я решил написать шаблонный класс C ++ для дерева quadtree.Я столкнулся с некоторой странной ошибкой компилятора, которую я не понимаю в отношении подклассов и указателей на шаблоны.Я нашел несколько хакерских обходных путей, но мне было интересно, кто-нибудь может пролить свет на то, почему мой код не компилируется ...


Я нахожусь в Linux, собираю с помощью scons, использую g ++

Мой код выглядит примерно так: у меня есть шаблонный класс для описания дерева и подкласс, описывающий «листья»:

template <class value_type>
class QuadTree
{

public:

    class Leaf //-Subclass--------------------------
    {
        friend class QuadTree< value_type >;
    protected:
        value_type* m_data;

        Leaf();
        ~Leaf();

    }; //-end-subclass------------------------------

    QuadTree();

    ~QuadTree();

    Leaf * Insert ( const value_type & _x );

protected:

    QuadTree( Quadtree< value_type >* _parent );

    QuadTree< value_type >* m_parent;

    QuadTree< value_type >* m_children[4];

    std::set< Leaf* > m_leaves;

};

Первая проблема с указателем, которую я получаю, заключается в деструкторе QuadTree:

template <class value_type>
QuadTree< value_type >::~QuadTree()
{
    // ... Delete children ...

    // I allocate each leaf, so I need to delete them
    std::set< Leaf* >::iterator it = m_leaves.begin(); // <-- bad
    std::set< Leaf* >::iterator endit = m_leaves.end(); // <-- bad
    for(;it != endit; ++it)
        delete *it;
}

Когда я компилирую, я получаю эту ошибку: expected ';' before ‘it’ и expected ';' before ‘endit’.Другая ошибка указателя находится в определении функции Вставить:

template <class value_type>
Leaf * QuadTree< value_type >::Insert ( const value_type & _x ) // <-- bad
{
    // Insert stuff...
}

Я получаю ошибку компиляции: expected constructor, destructor, or type conversion before ‘*’ token

Кто-нибудь знает, почему я получаю эти ошибки?У меня есть исправления для проблем, но я хочу знать, почему я не могу сделать это таким образом.

Ps.Я отредактировал код, чтобы показать его здесь, поэтому, возможно, я пропустил то, что мне показалось совершенно неуместным.

Редактировать.Исправлена ​​ошибка Quadtree -> QuadTree

Ответы [ 4 ]

4 голосов
/ 06 июля 2010

Вам нужно

typename std::set< Leaf* >::iterator it = m_leaves.begin(); 
typename std::set< Leaf* >::iterator endit = m_leaves.end();

Тип std :: set зависит от другого аргумента шаблона, и вы должны сообщить компилятору, что это на самом деле тип. gcc 4.5.0 выводит лучшее сообщение об ошибке.

Вторая ошибка похожа:

template <class value_type>
typename QuadTree<value_type>::Leaf* QuadTree< value_type >::Insert ( const value_type & _x )
{
    // Insert stuff...
}

Leaf является внутренним классом для QuadTree. Вам нужно назвать его так, и вам нужно указать тип QuadTree, поскольку внутренний класс зависит от параметра шаблона.

Еще одна вещь: у вас есть опечатка в QuadTree во многих местах.

1 голос
/ 06 июля 2010

Не связано ... но все же.

Класс Leaf является примером того, что вы не должны делать.

class Leaf //-Subclass--------------------------
{
    friend class QuadTree< value_type >;
protected:
    value_type* m_data;

    Leaf();
    ~Leaf();

}; //-end-subclass------------------------------
  • не используйте protected для атрибутов, это то же самое, что и public. Это означает, что вы не можете поддерживать какой-либо инвариант класса и, в частности, не можете гарантировать, что не будет никакой утечки
  • следует правилу Священного 3 : если вы пишете какой-либо из Деструктора, Конструктора Копии и Оператора Присвоения, вам нужно написать два других. Здесь конструктор копирования и оператор присваивания должны быть protected, чтобы предотвратить разрезание объектов (насколько это возможно), и они должны заботиться о памяти.

Теперь вопрос: почему вы используете указатель здесь?

class Leaf
{
public:
  explicit Leaf(value_type data): mData(data) {}

private:
  value_type mData;
};

Выглядит прекрасно и избавляет вас от утомительного обращения с динамически выделяемой памятью.

Кстати, ваш класс QuadTree страдает той же проблемой: в нем отсутствуют конструктор копирования и оператор присваивания.

1 голос
/ 06 июля 2010

В первом случае вам нужно сообщить компилятору, что std::set< Leaf* >::iterator является типом:

 typename std::set< Leaf* >::iterator it = ...
 typename std::set< Leaf* >::iterator endit = ...

Поскольку Leaf зависит (косвенно) от параметров шаблона, компилятор теперь точно не можеткакой именно класс Leaf окажется в итоге, и он не знает, будут ли специализации для std::set<Leaf*> и как эти специализации могут определять свои iterator.Поэтому компилятор предполагает, что iterator является нормальной переменной-членом std::set< Leaf* >, если только ключевым словом typename не указано иное.

Вторая проблема заключается в том, что при указании возвращаемого значения Leafеще не в области видимости ваших классов, и компилятор не знает, что вы ссылаетесь на вложенный класс.Вместо этого используйте полное имя класса:

template <class value_type>
typename Quadtree< value_type >::Leaf * Quadtree< value_type >::Insert (...) ...
1 голос
/ 06 июля 2010

Для исправления первой проблемы необходимо ключевое слово typename, например:

typename std::set< Leaf* >::iterator it = m_leaves.begin();

Вторая проблема вызвана тем, что Leaf и value_type не называют типы в этой строке. Необходимо указать, что вы имеете в виду Leaf и value_type из Quadtree< value_type >, например:

template <class value_type>
Quadtree< value_type >::Leaf * Quadtree< value_type >::Insert ( const typename Quadtree< value_type >::value_type & _x )
...