C++ - основной вопрос о контейнере

Как следует поступать в следующих случаях:

У меня есть геометрическое хранилище, имеющее тип вершины template from.

template <typename T> struct Geometry {

std::vector<T>& GetVertices() { ... }

const void* RawVertices() const { ... }

}

Это прекрасно работает, если только я не хочу хранить разные типы геометрии (например, Geometry<Vertex1> g1 и Geometry<Vertex2> g2 в одном контейнере.

Возможно ли это?

Или как мне реализовать хранилище геометрии (где я могу хранить и извлекать различные типы геометрии, используя один контейнер) или, может быть, как-то сопоставить тип T с типом Geometry<T>?

Любые советы?

Спасибо.


person Yippie-Ki-Yay    schedule 03.08.2010    source источник
comment
Кстати, как GetVertices() возвращает неконстантную ссылку из константной функции? Это выглядит немного изворотливым для меня.   -  person Mike Seymour    schedule 03.08.2010
comment
В любом случае, какой здесь вариант использования?   -  person tzaman    schedule 03.08.2010


Ответы (3)


Поскольку контейнер привязан к одному типу данных, которые он может содержать, вы можете создать класс GeometryBase, из которого будут получены все Geometry<T>, а затем сохранить в контейнере GeometryBase указатели.

struct GeometryBase
{
    // Non-template methods might go here.
    // Don't forget to declare the base class destructor virtual.
};

template <typename T> struct Geometry : public GeometryBase
{
    // Template methods go here
};

Изменить:
В какой-то момент вам придется решить, какой тип контейнера вершин вы хотите получить (мой подход) или что вы хотите делать с вершиной (подход Виджая Мэтью), а затем вы потребуется dynamic_cast‹>, чтобы получить доступ к методам производного класса.

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

class SomeStorageClass
{
/* ... */
private:
    std::vector< Geometry<Vertex1> > m_vertex1Geometries;
    std::vector< Geometry<Vertex2> > m_vertex2Geometries;
};

Если у вас есть функции, которые работают с одним типом геометрии (используя Vertex1::GetPos(), если использовать ваш пример) или с другим (Vertex2::GetUV()), то эти функции, вероятно, реализованы совершенно по-разному и, таким образом, заслуживают того, чтобы быть отдельными функциями, ожидающими разные типы параметров.

person foraidt    schedule 03.08.2010
comment
Не забудьте поставить виртуальный деструктор :) - person Vincent Robert; 03.08.2010
comment
И как мне реализовать эти GetVertices-подобные средства доступа, если я выберу твой путь? - person Yippie-Ki-Yay; 03.08.2010
comment
Мол, я больше не могу вернуть std::vector<T>&, а вызывать dynamic_cast для каждого из GeometryBase — это нонсенс... - person Yippie-Ki-Yay; 03.08.2010

Поскольку GetVertices будет возвращать только объекты типа Vertex, я предлагаю вам перейти к объектно-ориентированному дизайну от дженериков.

class Vertex
{
   ....
};

class Vertex1 : public Vertex 
{
   ....
};

class Vertex2 : public Vertex 
{
   ....
};

typedef std::vector<Vertex*> Vertices;

struct Geometry
{
    const Vertices& GetVertices() const { .... }
    ....
};
person Vijay Mathew    schedule 03.08.2010
comment
Я предполагаю, что ваш образец терпит неудачу, если я хочу, чтобы Vertex1 имел такие члены, как GetPos() и GetNormal() и Vertex2, чтобы также ввести метод GetUV(), который нельзя добавить в базовый суперкласс. - person Yippie-Ki-Yay; 03.08.2010
comment
@ HardCoder1986: он все еще работает, вам просто нужно преобразовать его в соответствующий тип во время выполнения. - person tzaman; 03.08.2010
comment
@HardCoder1986 динамическое_каст. Более того, эта проблема не была частью исходного вопроса. Мой ответ показывает общую идиому С++ для решения ситуации, подобной описанной. - person Vijay Mathew; 03.08.2010
comment
@tzaman И как мне определить тип, к которому я должен привести? (за исключением того факта, что понижение не очень круто :) - person Yippie-Ki-Yay; 03.08.2010
comment
@ HardCoder1986: Может быть, это не круто, но необходимо, если вы хотите получить объекты разных типов из однородного контейнера. - person tzaman; 03.08.2010

Гетерогенные контейнеры (т. е. хранящие более одного типа объектов) создают довольно много проблем — например, когда вы извлекаете объект, вам нужно что-то сделать, чтобы выяснить, какой тип объекта вы извлекаете.

Контейнеры, встроенные в C++, идут по простому пути: они однородны — они хранят только один тип объекта. Если вы хотите хранить объекты двух разных типов, вы должны обернуть их обоих в какой-то третий тип, который вы храните в контейнере (обычно вместе с чем-то, чтобы указать, к какому типу относится конкретный объект). Например, вы можете сделать что-то вроде этого:

class vertex1 {};

class vertex2 {};

class vertex {
    vertex1 *v1;
    vertex2 *v2;
public:
    vertex(vertex1 *init1) : v1(init1), v2(NULL) {}
    vertex(vertex2 *init2) : v1(NULL), v2(init2) {}
};

std::vector<vertex> vertices;

Конечно, вариаций много (например, хранение указателя на базовый класс), но в итоге все сводится к одному: сама коллекция содержит объект одного типа, а этот тип так или иначе управляет двумя другими. типы, с которыми вам нужно иметь дело.

person Jerry Coffin    schedule 03.08.2010
comment
Тип vertices должен быть std::vector<vertex*> или std::vector<vertex&> правильно? - person Vijay Mathew; 03.08.2010
comment
@Vijay: нет, не в этом случае. Вместо создания контейнера указателей мы создаем контейнер объектов, которые сами содержат пару указателей. Я разумно уверен, что контейнеры ссылок не разрешены. - person Jerry Coffin; 03.08.2010