Home > Mobile >  Access std::vector<std::variant> value by index
Access std::vector<std::variant> value by index

Time:08-29

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'; });
}
  •  Tags:  
  • c
  • Related