Замечания: Я знаю, что подобные вопросы были заданы на SO раньше, но я не нашел их полезными или очень ясными.
Второе примечание: В рамках данного проекта / задания я стараюсь избегать сторонних библиотек, таких как Boost.
Я пытаюсь понять, есть ли способ, которым я могу иметь один вектор для хранения нескольких типов в каждом из его индексов. Например, скажем, у меня есть следующий пример кода:
vector<something magical to hold various types> vec;
int x = 3;
string hi = "Hello World";
MyStruct s = {3, "Hi", 4.01};
vec.push_back(x);
vec.push_back(hi);
vec.push_back(s);
я слышал vector<void*>
может работать, но тогда это становится сложным с распределением памяти, и тогда всегда есть вероятность, что определенные части в соседней памяти могут быть непреднамеренно переопределены, если значение, вставленное в определенный индекс, больше, чем ожидалось.
В моем действительном заявлении я знаю, что возможный типы могут быть вставлены в вектор, но не все эти типы являются производными от одного и того же суперкласса, и нет никакой гарантии, что все эти типы будут помещены в вектор или в каком порядке.
Есть ли способ, которым я могу безопасно достичь цели, которую я продемонстрировал в моем примере кода?
Спасибо за ваше время.
Для этого вам определенно понадобится класс-обертка, чтобы каким-то образом скрыть информацию о типе ваших объектов от вектора.
Вероятно, также хорошо, чтобы этот класс генерировал исключение, когда вы пытаетесь вернуть Type-A, когда вы ранее сохранили в нем Type-B.
Вот часть класса Holder из одного из моих проектов. Вы, вероятно, можете начать отсюда.
Примечание: из-за использования неограниченных союзов это работает только в C ++ 11. Более подробную информацию об этом можно найти здесь: Какие неограниченные союзы предлагаются в C ++ 11?
class Holder {
public:
enum Type {
BOOL,
INT,
STRING,
// Other types you want to store into vector.
};
template<typename T>
Holder (Type type, T val);
~Holder () {
// You want to properly destroy
// union members below that have non-trivial constructors
}
operator bool () const {
if (type_ != BOOL) {
throw SomeException();
}
return impl_.bool_;
}
// Do the same for other operators
// Or maybe use templates?
private:
union Impl {
bool bool_;
int int_;
string string_;
Impl() { new(&string_) string; }
} impl_;
Type type_;
// Other stuff.
};
Объекты, удерживаемые std::vector<T>
должны быть однородного типа. Если вам нужно поместить объекты разного типа в один вектор, вам нужно как-то стереть их тип и сделать их все похожими. Вы можете использовать моральный эквивалент boost::any
или же boost::variant<...>
, Идея boost::any
заключается в том, чтобы инкапсулировать иерархию типов, сохраняя указатель на базу, но указывая на шаблонизированный производный. Очень грубый и неполный контур выглядит примерно так:
#include <algorithm>
#include <iostream>
class any
{
private:
struct base {
virtual ~base() {}
virtual base* clone() const = 0;
};
template <typename T>
struct data: base {
data(T const& value): value_(value) {}
base* clone() const { return new data<T>(*this); }
T value_;
};
base* ptr_;
public:
template <typename T> any(T const& value): ptr_(new data<T>(value)) {}
any(any const& other): ptr_(other.ptr_->clone()) {}
any& operator= (any const& other) {
any(other).swap(*this);
return *this;
}
~any() { delete this->ptr_; }
void swap(any& other) { std::swap(this->ptr_, other.ptr_); }
template <typename T>
T& get() {
return dynamic_cast<data<T>&>(*this->ptr_).value_;
}
};
int main()
{
any a0(17);
any a1(3.14);
try { a0.get<double>(); } catch (...) {}
a0 = a1;
std::cout << a0.get<double>() << "\n";
}
Как и предполагалось, вы можете использовать различные формы союзов, вариантов и т. Д. В зависимости от того, что вы хотите сделать с вашими хранимыми объектами, внешний полиморфизм может делать именно то, что вы хотите если вы можете определить все необходимые операции в интерфейсе базового класса.
Вот пример, если все, что мы хотим сделать, это вывести объекты на консоль:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class any_type
{
public:
virtual ~any_type() {}
virtual void print() = 0;
};
template <class T>
class concrete_type : public any_type
{
public:
concrete_type(const T& value) : value_(value)
{}
virtual void print()
{
std::cout << value_ << '\n';
}
private:
T value_;
};
int main()
{
std::vector<std::unique_ptr<any_type>> v(2);
v[0].reset(new concrete_type<int>(99));
v[1].reset(new concrete_type<std::string>("Bottles of Beer"));
for(size_t x = 0; x < 2; ++x)
{
v[x]->print();
}
return 0;
}