c++c++20std-variant

"increment" `std::variant` alternative


I want to increment / decrement a std::variant's type alternative, essentially like so:

using var_t = std::variant</*...*/>;
var_t var;
var.emplace< (var.index()+1) % std::variant_size<var_t> >(); // "increment" case, wrapping for good measure

The problem here is that while emplace expects what clang's error message calls an "explicitly-specified argument", index does not appear to be constexpr.

The obvious alternative would be something like this:

switch(var.index()){
  0:
    var.emplace<1>();
    break;
  1:
    var.emplace<2>();
    break;
// ...
  variant_size<var_t>-1:
    var.emplace<0>();
}

But that's what I personally would call "extremely ugly" and "a massive pain in the behind to maintain" (especially since I'd have to maintain two almost-copies of those blocks off-by-two for both incrementing and decrementing).

Is there a better / "correct" way of doing this?

In case that information is important in any way, I'm targeting C++20 on clang with libstdc++.


Solution

  • As usual, std::index_sequence might help:

    #include <variant>
    
    template <typename... Ts, std::size_t... Is>
    void next(std::variant<Ts...>& v, std::index_sequence<Is...>)
    {
        using Func = void (*)(std::variant<Ts...>&);
        Func funcs[] = {
            +[](std::variant<Ts...>& v){ v.template emplace<(Is + 1) % sizeof...(Is)>(); }...
        };
        funcs[v.index()](v);
    }
    
    template <typename... Ts>
    void next(std::variant<Ts...>& v)
    {
        next(v, std::make_index_sequence<sizeof...(Ts)>());
    }
    

    Demo

    Note: for prev, Is + 1 should be replaced by Is + sizeof...(Is) - 1.