Вектор, который может иметь 3 разных типа данных С++

Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных. Я не хочу использовать библиотеку boost. Что-то вроде:

vector<type1, type2, type3> vectorName; 

Нужно ли создавать шаблон? И если да, как бы я это сделал?

Ответ 1

ОБНОВЛЕНИЕ: начиная с C++ 17, стандартная библиотека теперь включает шаблон класса std::варианта, который очень похож на ранее существующие решения в boost. variant представляет собой безопасную альтернативу типам для объединений, которая позволяет объединять несколько типов с использованием отношения "или", например, std::variant<type1, type2, typ3> содержит либо type1, либо type2, либо a type3.. Может быть составлено с std::vector, чтобы получить именно то, что вы описали:

std::vector<std::variant<type1, type2, type3>> vectorName; 

Однако, std::variant вводит некоторые ограничения. Например, он не может содержать типы ссылок или массивов, а базовый тип (т.е. type1 или type2) может быть доступен только с помощью кода шаблона. Если std::variant не разрешает конкретное поведение, которое вам нужно, продолжайте чтение для более сложного, но более универсального подхода, который также имеет преимущество работы в любой версии C++.

ОРИГИНАЛЬНЫЙ ОТВЕТ:

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

class Parent
{
  //anything common to the types should be declared here, for instance:
  void print() //make this virtual if you want subclasses to override it
  {
     std::cout << "Printing!";
  }

  virtual ~Parent(); //virtual destructor to ensure our subclasses are correctly deallocated
};

class Type1 : public Parent
{
    void type1method();
};

class Type2 : public Parent
{
    void type2Method();
};


class Type3 : public Parent
{
    void type3Method();
};

Затем вы можете создать вектор указателей Parent, который может хранить указатели на дочерние типы:

std::vector<Parent*> vec;

vec.push_back(new Type1);
vec.push_back(new Type2);
vec.push_back(new Type3);

При доступе к элементам непосредственно из вектора вы сможете использовать только элементы, принадлежащие Parent. Например, вы можете написать:

vec[0]->print();

Но не:

vec[0]->type1Method();

Поскольку тип элемента был объявлен как Parent*, а тип Parent не имеет type1Method.

Если вам нужен доступ к элементам, относящимся к подтипу, вы можете преобразовать указатели Parent в указатели подтипов следующим образом:

Parent *p = vec[0];

Type1 *t1 = nullptr;
Type2 *t2 = nullptr;
Type3 *t3 = nullptr;

if (t1 = dynamic_cast<Type1*>(p))
{
    t1->type1Method();
}
else if (t2 = dynamic_cast<Type2*>(p))
{
    t2->type2Method();
}
else if (t3 = dynamic_cast<Type3*>(p))
{
    t3->type3Method();
}

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

Обязательно удалите указатели, прежде чем удалять их из вектора, если вы используете динамическое размещение, как я делал в примере выше. В качестве альтернативы, используйте умные указатели (вероятно, std::unique_ptr) и позвольте вашей памяти позаботиться о себе:

std::vector<std::unique_ptr<Parent>> vec;

Ответ 2

Я пытаюсь создать вектор на С++, который может хранить 3 разных типа данных.

Ответ здесь действительно зависит от конкретного варианта использования:

  • Если объекты каким-то образом связаны и похожи в некотором роде - создайте базовый класс и извлеките из него все классы, затем создайте векторный хранилище unique_ptr для родительского класса (см. ApproachingDarknessFish s для деталей),

  • Если все объекты являются фундаментальными (так встроенными) типами - используйте union, который группирует типы и определяет vector<yourUnionType>,

  • Если объекты неизвестного типа, но вы уверены, что они имеют сходный интерфейс, создайте базовый класс и вытащите из него шаблонный дочерний класс (template <typename T> class container: public parent{};) и создайте vector<unique_ptr<parent>> как в первом случае,

  • Если объекты относятся к типам, которые по какой-либо причине не могут быть подключены (например, vector сохраняет int, std::string и yourType), подключите их через union, как в 2. Или - еще лучше...

... если у вас есть время и вы хотите что-то узнать - посмотрите, как реализуется boost::any и попытайтесь реализовать его самостоятельно, если вы действительно не хотите использовать сама библиотека. Это не так сложно, как может показаться.

Ответ 3

вы можете использовать std :: any, хранить ваши объекты в векторе как любые, а когда вы их извлекаете, используйте type() == typeid (mytype)

https://en.cppreference.com/w/cpp/utility/any

Это только для С++ 17 и выше.

Ответ 4

Это должен быть вектор? Вы можете просто рассмотреть связанный список универсального типа, затем выполнить итерацию по этому списку, использовать typeid(), чтобы выяснить тип данных узла, и получить данные с помощью функции node.get().