Search code examples
c++c++17stdvectorstdsetstd-variant

How to filter out elements of certain data types in a vector of std::variant?


I have a std::vector of std::variant elements with type int or std::set<int>. I want to loop over this vector and insert an extra item if the iterated element is of type std::set<int>. However, it seems that querying the index at run-time is not allowed. How can I achieve this?

#include <variant>
#include <set>
#include <vector>

int main()
{
    using Variants = std::variant<int, std::set<int>>;

    std::vector<Variants> var_vec;
    var_vec.push_back(999);
    std::set<int> a = {0,1,2};
    var_vec.push_back(a);

    for (int i = 0; i < var_vec.size(); ++i)
    {
        // if the element var_vec[i] is of type std::set<int>
        if (var_vec[i].index() == 1) var_vec[i].insert(888);   // !ERROR! How to achieve this?
    }
    
    return 0;
}

Error message:

error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'

Solution

  • There's nothing wrong with calling index() at runtime, this happens all the time. The real problem is:

    var_vec[i].insert(888);
    

    var_vec[i] is a std::variant. It does not have a method called insert().

    std::get<1>(var_vec[i]).insert(888);
    

    This gives you the variant's set, which will happily allow you to insert() something.

    One problem with this overall approach is that if you, for some reason, want to modify your variant, and that std::set is no longer its 2nd alternative, for some reason, all of the above logic will break again.

    You should consider using std::holds_alternative, instead of index(), and using a type with std::get instead of an index, which will automatically adapt to this kind of a change.