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
相关产品推荐
相关产品推荐

