Как я могу хранить объекты разных типов в контейнере C ++? - PullRequest
25 голосов
/ 19 января 2011

Есть ли контейнер C ++, который я мог бы использовать или построить, который может содержать, скажем, типы int и string и double? Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я пытаюсь заполнить, скажем, карту, вектор или список, скажем, следующим:

int x;
string y;
double z;

Я ограничен форматом:

list<int> mycountainer;
vector<string> mycontainer;

, что заставляет mycontainer состоять только из одного типа.

Прежде чем кто-либо предложит генерики, они тоже не будут работать, поскольку стандартные контейнеры vector и list, поставляемые с C ++ , уже являются универсальными - они могут быть контейнерами для любых типов, но не могут содержать несколько типы.

Я бы тоже хотел избегать использования Boost, если это вообще возможно - я бы предпочел, если бы был простой способ написать это сам.

[править] Эй, парень, большое спасибо за ваши предложения - я должен объяснить, как я буду использовать этот контейнер, но он немного сложен, поэтому (большое) упрощение приведено выше. Я думаю, что лучший вариант здесь использует Boost. Еще раз спасибо.

Ответы [ 7 ]

22 голосов
/ 19 января 2011

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

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

Мне любопытно, что вы делаете, но вам нужна такая конструкция.

13 голосов
/ 19 января 2011

Ну, первый вопрос: Почему вы думаете, что вам нужно хранить объекты разных, совершенно не связанных между собой типов в одном контейнере? Мне это кажется подозрительным.

Если бы у меня была необходимость, я бы посмотрел на boost::variant или boost::any.

6 голосов
/ 19 января 2011

То, что вы хотите, называется «гетерогенный контейнер».C ++ технически не поддерживает их в STL, но Boost поддерживает.

Учитывая это, я думаю, вы найдете свой ответ в этом вопросе: how-do-you-make-a-гетерогенный-boostmap

3 голосов
/ 19 января 2011

Вы можете использовать либо структуры, либо классы, либо std :: pair.

[править]

Для классов и структур:

struct XYZ {
    int x;
    string y;
    double z;
};
std::vector<XYZ> container;

XYZ el;
el.x = 10;
el.y = "asd";
el.z = 1.123;
container.push_back(el);

Для std :: pair:

#include <pair>
typedef std::pair<int, std::pair<string, double> > XYZ;
std::vector<XYZ> container;
container.push_back(std::make_pair(10, std::make_pair("asd", 1111.222)));
1 голос
/ 19 января 2011

Вы можете использовать структуру, которая содержит все три.

struct Data
{
    int intVal;
    std::string stringVal;
    double doubleVal;
};

Тогда вы можете просто объявить list mycontainer<Data> и использовать соответствующее значение, при условии, что вы знаете тип значения. Если нет, добавьте в структуру поле дополнения, которое сообщит вам, какой из трех типов данных используется.

struct Data
{
    enum DATATYPE { DT_INT, DT_STRING, DT_DOUBLE } type;

    int intVal;
    std::string stringVal;
    double doubleVal;
};

Если вы беспокоитесь об использовании памяти, возможно, вы могли бы использовать объединение, хотя я стараюсь не использовать их. Это может быть ненужной паранойей с моей стороны.

0 голосов
/ 19 января 2011

Самым простым методом является, конечно, определение структуры или класса, в котором есть члены каждого из типов, которые вы хотите сохранить. ответ Джоша предлагает Boost.Any , который будет содержать в значительной степени что угодно . Если вы хотите ограничить значения только значениями типов int, double и std::string, то лучшим выбором будет Boost.Variant .

Если вы просто не хотите, чтобы использовал Boost, тогда я советую вам прекратить зависания и использовать его в любом случае. «Не изобретено здесь» - это саморазрушительная политика. Но если вы не можете использовать Boost, тогда вы можете написать свой собственный вариантный класс. Андрей Александреску написал серию из трех частей ( часть 1 , часть 2 , часть 3 ) несколько лет назад, и его дизайн вдохновил один Boost использует.

0 голосов
/ 19 января 2011

Если у вас есть конечное количество предметов, которые нужно хранить, поместите их в класс или структуру.

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

Однако, если какой-либо предмет потенциально может находиться в контейнере, то у вас нет возможности узнать, какие конкретно типы элементов находятся в контейнере, и, следовательно, вы не сможете их разыграть.

Если бы в C ++ содержалось отражение, возможно, был бы способ сделать это, но в C ++ нет отражения.

...