如何在std::variant访问器中仅生成同类型组合的代码
避免std::variant访问器生成类型不匹配组合代码的方案
已知外部已保证std::variant类型的lhs和rhs底层存储类型完全匹配,但使用std::visit时会默认生成所有类型组合的代码(如示例中的int-double、double-int这类无效组合),需要找到最佳方式排除这些无用代码的生成。
示例代码:
using StorageT = std::variant<std::vector<int>, std::vector<double>>; StorageT process_data(const StorageT &lhs, const StorageT &rhs) { return std::visit([&](auto &&l, auto &&r){ return kernel(l, r); // 模板化的kernel函数 }, lhs, rhs); }
方案一:直接索引匹配(适合类型数量少的场景)
利用variant的索引特性,结合switch直接处理同类型的情况,完全不会生成类型不匹配的代码:
#include <variant> #include <vector> #include <cassert> #include <stdexcept> using StorageT = std::variant<std::vector<int>, std::vector<double>>; StorageT process_data(const StorageT &lhs, const StorageT &rhs) { // 外部已验证,此处可加断言防止意外情况 assert(lhs.index() == rhs.index()); switch(lhs.index()) { case 0: { const auto& l_vec = std::get<0>(lhs); const auto& r_vec = std::get<0>(rhs); return kernel(l_vec, r_vec); } case 1: { const auto& l_vec = std::get<1>(lhs); const auto& r_vec = std::get<1>(rhs); return kernel(l_vec, r_vec); } default: // C++20及以上可使用std::unreachable()替代抛出异常 throw std::logic_error("Unexpected variant index mismatch"); } }
这种方式逻辑直观,编译时只会生成同类型的处理代码,无冗余开销。
方案二:元编程生成同类型访问器(适合类型数量多的场景)
当variant包含大量类型时,手动写switch会产生冗余代码,可通过元编程自动生成同类型的处理逻辑:
#include <variant> #include <vector> #include <cassert> #include <utility> using StorageT = std::variant<std::vector<int>, std::vector<double>>; // 辅助函数:根据索引生成同类型处理逻辑 template<typename Kernel, typename Variant, std::size_t... Is> auto visit_same_type_impl(const Variant& lhs, const Variant& rhs, Kernel&& kernel, std::index_sequence<Is...>) { assert(lhs.index() == rhs.index()); using RetType = decltype(kernel(std::get<0>(lhs), std::get<0>(rhs))); // 生成对应每个索引的处理函数数组 const auto process_funcs = { +[](const Variant& lhs, const Variant& rhs, Kernel&& kernel) -> RetType { return kernel(std::get<Is>(lhs), std::get<Is>(rhs)); }... }; // 调用对应索引的处理函数 return (*std::next(process_funcs.begin(), lhs.index()))(lhs, rhs, std::forward<Kernel>(kernel)); } // 对外接口:包装元编程逻辑 template<typename Kernel, typename Variant> auto visit_same_type(const Variant& lhs, const Variant& rhs, Kernel&& kernel) { return visit_same_type_impl(lhs, rhs, std::forward<Kernel>(kernel), std::make_index_sequence<std::variant_size_v<Variant>>{}); } // 业务函数 StorageT process_data(const StorageT &lhs, const StorageT &rhs) { return visit_same_type(lhs, rhs, [](const auto& l, const auto& r) { return kernel(l, r); }); }
该方案通过编译期生成处理函数数组,仅保留同类型的调用逻辑,同时避免了手动编写大量重复代码。
方案三:受限访问器类(编译期检查同类型)
定义仅接受同类型参数的访问器类,让std::visit只匹配有效组合:
#include <variant> #include <vector> using StorageT = std::variant<std::vector<int>, std::vector<double>>; // 仅处理同类型参数的访问器 struct SameTypeVisitor { template<typename T> StorageT operator()(const T& l, const T& r) const { return kernel(l, r); } }; StorageT process_data(const StorageT &lhs, const StorageT &rhs) { // 外部已保证类型匹配,若意外出现不匹配会触发编译/运行错误 return std::visit(SameTypeVisitor{}, lhs, rhs); }
注:若外部验证失效,此方案会因无对应重载导致编译错误(或运行时std::bad_variant_access,取决于编译器),适合完全信任外部验证的场景。
内容的提问来源于stack exchange,提问作者JakeTuero
相关产品推荐
相关产品推荐

