I need to store a series of data-points in the form of (name, value), where the value could take different types.
I am trying to use a class template for each data-point. Then for each data-point I see, I want to create a new object and push it back into a vector. For each new type, I need to create a new class from the template first. But I can not store the objects created in any vector, since vectors expect the same type for all entries. The types I need to store can not be fitted in a inheritance hierarchy. They are unrelated. Also there can be more types created in future, and I do not want to change the storage service for each new type. Is there a way to create a heterogeneous container to store these entries? Thank you!
std::any
allows to hold any type, although it requires knowing the type that was stored to retrieve it.
If you have a set of known types, however, you may prefer std::variant
:
using variant_type = std::variant<Foo, Bar, Joe>;
int func(variant_type const& v) // not template
{
auto const visitor = [](auto const& t)
{
if constexpr (std::is_same_v<Foo const&, decltype(t)>)
{
return t.fooish();
}
else
{
return t.barjoeish();
}
};
return std::visit(visitor, v);
}
A useful trick for quickly defining visitors:
template <typename... Ts> struct overload : Ts...
{
overload(Ts... aFns) : Ts(aFns)... {}
using Ts::operator()...;
};
template <typename... Ts> overload(Ts...) -> overload<Ts...>;
// Used as
auto const visitor = overload(
[](Foo const& foo) { return foo.fooish(); },
[](auto const& other) { return other.joebarish(); }
);
return std::visit(visitor, variant);
boost::any
has already been recommended, however it's for anything, so you can't expect much from it.
If you know the various types ahead of time, you're better using boost::variant
.
typedef boost::variant<Foo, Bar, Joe> variant_type;
struct Print: boost::static_visitor<>
{
void operator()(Foo const& f) const { f.print(std::cout); }
template <class T>
void operator()(T const& t) const { std::cout << t << '\n'; }
};
void func(variant_type const& v) // not template
{
boost::apply_visitor(Print(), v); // compile-time checking
// that all types are handled
}
The boost library has probably what you're looking for (boost::any). You can roll your own using a wrapped pointer approach if you cannot use boost...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With