You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20中如何优雅实现std::variant可选项类型的递增与递减操作

std::variant 递增/递减切换的优雅实现

核心思路

利用C++20的模板特性在编译期生成所有下标对应的处理函数跳转表,运行时直接通过variant::index()的返回值查表调用,完全避免手写switch的冗余代码,变体类型数量变化时自动适配,不需要修改工具逻辑。

完整代码实现

#include <variant>
#include <array>
#include <utility>

// 递增切换variant类型
template <typename VarT, std::size_t... Is = std::make_index_sequence<std::variant_size_v<VarT>>()>
void variant_incr(VarT& var)
requires (std::is_default_constructible_v<std::variant_alternative_t<Is, VarT>> && ...)
{
    static constexpr auto handlers = []<std::size_t... Idx>(std::index_sequence<Idx...>) {
        return std::array{
            +[](VarT& v) {
                constexpr std::size_t next_idx = (Idx + 1) % sizeof...(Idx);
                v.template emplace<next_idx>();
            }...
        };
    }(std::make_index_sequence<std::variant_size_v<VarT>>{});

    handlers[var.index()](var);
}

// 递减切换variant类型
template <typename VarT, std::size_t... Is = std::make_index_sequence<std::variant_size_v<VarT>>()>
void variant_decr(VarT& var)
requires (std::is_default_constructible_v<std::variant_alternative_t<Is, VarT>> && ...)
{
    static constexpr auto handlers = []<std::size_t... Idx>(std::index_sequence<Idx...>) {
        return std::array{
            +[](VarT& v) {
                constexpr std::size_t prev_idx = (Idx + sizeof...(Idx) - 1) % sizeof...(Idx);
                v.template emplace<prev_idx>();
            }...
        };
    }(std::make_index_sequence<std::variant_size_v<VarT>>{});

    handlers[var.index()](var);
}

使用示例

#include <string>

int main() {
    using var_t = std::variant<int, float, std::string>;
    var_t v = 10; // 当前是下标0的int类型
    
    variant_incr(v); // 切换到下标1的float类型
    variant_incr(v); // 切换到下标2的std::string类型
    variant_incr(v); // 循环回到下标0的int类型
    
    variant_decr(v); // 切换回下标2的std::string类型
    variant_decr(v); // 切换回下标1的float类型
}

注意事项

  • 上述实现和你最初的代码逻辑对齐,默认构造新类型的实例,如果需要自定义构造参数,可修改lambda的参数列表传入构造参数即可。
  • 如果备选类型的默认构造函数可能抛出异常,emplace调用失败时variant会进入无值状态;如需强异常安全,可先构造新类型的临时对象,再通过operator=赋值给variant,抛出异常时原variant状态不受影响。
  • 编译期生成的跳转表是静态常量,运行时开销和手写switch完全一致,经clang优化后生成的汇编代码和手写switch没有差异,无额外性能损失。

内容的提问来源于stack exchange,提问作者nonchip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:18:01