“增量”`std::variant` 替代方案

non*_*hip 2 c++ c++20 std-variant

我想增加/减少 astd::variant的类型替代,基本上像这样:

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

这里的问题是,虽然emplace期望 clang 的错误消息称为“显式指定的参数”,index但似乎不是constexpr.

明显的替代方案是这样的:

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

但这就是我个人所说的“极其丑陋”和“维护背后的巨大痛苦”(特别是因为我必须维护这些块的两个几乎副本,以进行递增和递减)。

有没有更好/“正确”的方法来做到这一点?

如果该信息在任何方面都很重要,我的目标C++20是.clanglibstdc++

Jar*_*d42 5

像往常一样,std::index_sequence可能会有所帮助:

#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)>());
}
Run Code Online (Sandbox Code Playgroud)

演示

:for prevIs + 1应替换为Is + sizeof...(Is) - 1


Hol*_*olt 5

另一种可能的解决方案(在我看来)比 @Jarod42 的方案有点难看,并且依赖于在编译时使用模板化 lambda 查找索引std::visit

#include <variant>

template <class T, std::size_t I, class... Args>
struct index_of_;

template <class T, std::size_t I, class... Args>
struct index_of_<T, I, T, Args... >: std::integral_constant<std::size_t, I> {};

template <class T, std::size_t I, class U, class... Args>
struct index_of_<T, I, U, Args... >: index_of_<T, I + 1, Args... > {};

template <class T, class... Args>
struct next_index: std::integral_constant<
    std::size_t, 
    (index_of_<T, 0, Args... >::value + 1) % sizeof... (Args)> {};

template <class... Args>
void increment(std::variant<Args...>& variant) {
    // prior to C++20, you can use [&](auto const& arg) and retrieve T
    // via std::decay_t<decltype(arg)>, 
    std::visit([&]<class T>(T const&) {
        variant.template emplace<next_index<T, Args...>::value>();
    }, variant);
}
Run Code Online (Sandbox Code Playgroud)

与 @Jarod42 解决方案不同,如果您的变体中有重复的类型,则此解决方案将不起作用。