I would like to access a member of std::vector<std::variant>
by index. Considering the following snippet:
struct Data {
using data_types = std::variant<std::basic_string<char>, double, int>;
public:
template <class T>
void push_back(const T& t) {
m_data.push_back(t);
}
private:
std::vector<data_types> m_data;
};
int main()
{
Data d;
d.push_back(0);
d.push_back("string");
d.push_back(3.55);
}
I would like to access the values like d[0]
(should return int
) or d[1]
(should return std::string
).
What I have tried so far but what isn't working is to add the following public method to the existing struct:
template <class T>
T& operator[](const size_t &index) {
return std::visit([](const T& value) {
return static_cast<T>(value);
}, m_data[index]);
}
Any ideas how to achieve the desired result?
CodePudding user response:
The type of an expression in C cannot depend on runtime parameters; basically it can only depend on types of the arguments, plus non-type template arguments.
So d[0]
and d[1]
must have the same type, as the type of the pieces of the expression are identical, and there are no non-type template arguments.
std::get<int>(d[0])
vs std::get<double>(d[1])
can differ in type.
std::get<1>(d[0])
vs std::get<2>(d[1])
can differ in type.
std::visit
is a mechanism used to get around this; here, we create every a function object call, one for each possible type, and then pick one at runtime to actually call. However, the type returned from the visit
still follows the above rule: it doesn't depend on what type is stored in the variant
, and every possible type in the variant
must have a valid instantiation of the function.
C type system is not a runtime type system. It is compile-time. Stuff like variant
and dynamic_cast
and any
give some runtime exposure to it, but it is intentionally minimal.
CodePudding user response:
To extract a value from variant
, use std::get
:
struct Data
{
...
template <class T>
T& operator[](size_t index)
{
return std::get<T>(m_data[index]);
}
};
However, because this overloaded operator is a template, you can't use simple operator syntax to call it. Use the verbose syntax:
int main()
{
Data d;
d.push_back(0);
d.push_back("string");
d.push_back(3.55);
std::cout << d.operator[]<double>(2);
}
Or rename it to use a plain name instead of the fancy operator[]
.
CodePudding user response:
Visitor pattern:
#include <iostream>
#include <string>
#include <variant>
#include <vector>
template <class ...Ts>
struct MultiVector : std::vector<std::variant<Ts...>> {
template <class Visitor>
void visit(std::size_t i, Visitor&& v) {
std::visit(v, (*this)[i]);
}
};
int main() {
MultiVector<std::string, int, double> vec;
vec.push_back(0);
vec.push_back("string");
vec.push_back(3.55);
vec.visit(2, [](auto& e) { std::cout << e << '\n'; });
}