Вы можете использовать стирание типа.
struct ContainerBase
{
virtual ~ContainerBase() = 0;
// This is where you can add an interface for common functionality.
// Write the pure virtual functions here and implement/override them in ContainerTyped.
};
inline ContainerBase::~ContainerBase() = default;
template<class T>
struct ContainerTyped : public ContainerBase
{
std::vector<T> values;
};
class Cont
{
std::vector<std::unique_ptr<ContainerBase>> containers;
public:
Cont(int mask) {
// ...
if ((mask & fooMask) > 0)
containers.push_back(std::make_unique<ContainerTyped<Foo>>());
if ((mask & barMask) > 0)
containers.push_back(std::make_unique<ContainerTyped<Bar>>());
}
};
Демо
Это, вероятно, более подходит, чем, например, использовать std::any
или стирание другого существующего типа, потому что 1) вы указываете, что только определенные вещи (ваши векторные контейнеры) могут быть сохранены, и 2) вы можете добавить общий интерфейс, как указано, и даже специализировать функции интерфейса по-другому ContainerTyped
. Но нам нужно больше узнать о вашем сценарии использования, чтобы подробно описать это преимущество.
Проблема с void*
всегда заключается в том, что вам нужно каким-то образом сохранять информацию о том, что вы на самом деле хранили, потому что вы обходите систему строгого типа. Другими словами, как бы вы вернули сохраненную вещь обратно в систему сильных типов? Это как раз та часть, где вышеприведенный подход может проявиться, потому что вы можете добавить virtual print() = 0
в ContainerBase
, а затем создать специализированные версии для каждого вида структуры, например,
template<>
void ContainerTyped<Foo>::print()
{
for (Foo& foo : values) {
// Print Foo objects as you wish!
}
}
С точки зрения того, что вам не нужно прикасаться к конструктору Cont
при добавлении структуры Qux
, вам, очевидно, все равно необходимо кодировать информацию о том, «какой бит маски принадлежит какой-либо структуре», но вы можете извлечь его из Cont
конструктор (и даже скрыть его в другом модуле перевода):
// Put this implementation wherever, Cont only has to know the signature.
std::unique_ptr<ContainerBase> makeContainer(int mask, unsigned indexBit)
{
if ((mask & fooMask) > 0)
return std::make_unique<ContainerTyped<Foo>>();
// etc.
if ((mask & quxMask) > 0)
return std::make_unique<ContainerTyped<Qux>>();
return nullptr;
}
// ...
Cont::Cont(int mask)
{
for (unsigned indexBit = 0; indexBit < 8; ++indexBit) {
auto container = makeContainer(mask, indexBit);
if (container)
containers.emplace_back(std::move(container));
}
}
Вы можете использовать другие способы кодирования этого enum-> type type, но это выходит за рамки этого вопроса. Ключ в том, что вы можете скрыть свой конкретный тип за ContainerBase
и использовать его везде, где вы хотите сослаться на «любой из этих контейнеров».