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'; });
}
