контейнер несвязанных T в C ++ - PullRequest
1 голос
/ 22 августа 2011

Если у меня есть следующий гипотетический класс:

namespace System
{
    template <class T>
    class Container
    {
    public:
        Container() { }
        ~Container() { }
    }
}

Если я создаю два контейнера с разными T, скажем:

Container<int> a;
Container<string> b;

Я хотел бы создать вектор с указателями наи б.Поскольку a и b - это разные типы, обычно это невозможно.Тем не менее, если бы я сделал что-то вроде:

std::stack<void*> _collection;
void *p = reinterpret_cast<void*>(&a);
void *q = reinterpret_cast<void*>(&b);
_collection.push(a);
_collection.push(b);

Затем я смогу получить a и b из _collection следующим образом:

Container<string> b = *reinterpret_cast<Container<string>*>(_collection.pop());
Container<int> a = *reinterpret_cast<Container<int>*>(_collection.pop());

Мой вопрос: это лучшийспособ хранения коллекции несвязанных типов?Также будет ли это предпочтительным способом хранения и извлечения указателей из вектора (переинтерпретация)?Я огляделся и увидел, что у Boost есть более хороший способ решения этой проблемы, Boost :: Any, но так как это учебный проект, в котором я участвую, я хотел бы сделать это сам (также мне было любопытно найти вескую причинуправильно использовать reinterpret_cast).

Ответы [ 2 ]

6 голосов
/ 22 августа 2011

Рассмотрим boost::any или boost::variant, если вы хотите хранить объекты разнородных типов.

И прежде чем решить, какой из них использовать, посмотритепри сравнении:

Надеемся, это поможет вам принять правильное решение.Выберите один и любой контейнер из стандартной библиотеки для хранения объектов, std::stack<boost::any>, std::stack<boost::variant> или любой другой. Не пишите свой собственный контейнер.

Я повторяю Не пишите свой собственный контейнер .Используйте контейнеры из стандартной библиотеки.Они хорошо проверены.

4 голосов
/ 22 августа 2011

Хотя возможно привести к void * и обратно, проблема в том, что вы знаете, какой тип вы используете. Ведь вы приводите пример:

Container<string> b = *reinterpret_cast<Container<string>*>(_collection.pop());
Container<int> a = *reinterpret_cast<Container<int>*>(_collection.pop());

Однако, если бы вы случайно сделали:

Container<int> a = *reinterpret_cast<Container<int>*>(_collection.pop());
Container<string> b = *reinterpret_cast<Container<string>*>(_collection.pop());

Теперь у вас есть указатели на неправильный тип, и вы, скорее всего, увидите сбои - или еще хуже.

Если вы хотите сделать что-то подобное, по крайней мере используйте dynamic_cast, чтобы проверить, что у вас есть нужные типы. С dynamic_cast вы можете иметь проверку C ++ во время выполнения (используя RTTI ), что ваше приведение является безопасным, если типы приведения (как до, так и после) имеют общий базовый тип с at хотя бы один виртуальный метод.

Итак, сначала создайте общий базовый тип с виртуальным деструктором:

class ContainerBase {
public:
  virtual ~ContainerBase() { }
};

Сделайте ваши контейнеры производными от него:

template <typename T>
class Container : public ContainerBase {
// ...
}

Теперь используйте std::stack<ContainerBase *>. Когда вы получаете предметы из стека, используйте dynamic_cast<Container<int> >(stack.pop()) или dynamic_cast<Container<string> >(stack.pop()); если у вас неправильные типы, они проверит и вернут NULL.

Тем не менее, гетерогенные контейнеры почти всегда неправильно использовать; на каком-то уровне вам нужно знать, что находится в контейнере, чтобы вы могли использовать его. Чего вы на самом деле пытаетесь достичь, создав такой контейнер?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...