基于std::ranges管道风格的std::variant访问器实现求助
问题:实现带编译时检查的std::variant链式访问器
受std::ranges和std::view的管道操作符(|)启发,希望封装std::get_if和std::visit模式,实现一个**可原地构造、非修改(支持constexpr)**的包装器,用于处理std::variant<...>(包括std::optional<std::variant<...>>),允许通过lambda指定对应类型的处理逻辑。
设计思路采用「可选变体访问器视图」的链式单子方案:
- 用
<<操作符处理nullopt场景(适配C17-C20) - 计划用C20模板概念替代C14风格写法
期望用法示例
int main() { std::optional<std::variant<int, float, bool>> myVariant = 42; /* 注:构建调用链时可任意排序,但不可重复添加同一类型的处理,如多次传入type<int>,第二次应触发静态断言“int类型已被处理” */ variants(myVariant) << no_option([]() { /* 处理variant为std::nullopt的逻辑,无需可注释*/; }) | on_type<bool>([](const auto& value) {}) | on_type<int>([](const auto& value) {}) // | on_type<string>([](const auto& value) {}) // 应触发静态断言:类型不在variant成员列表中 | on_type<float>([](const auto& value) {}) // | on_type<int>([](const auto& value) {}) // 应触发静态断言:处理链中存在重复类型 | no_type(); // 若variant存在未处理的类型,链结束检查器会触发静态断言,无需强制处理可注释 return 0; }
现有代码实现(存在编译时检查缺陷)
template <typename T> struct on_type { using type_t = T; on_type(std::function<void(const T&)> callback) : callback_func(callback) {} void if_type(const T& value) const { callback_func(value); } std::function<void(const T&)> callback_func; }; struct no_type { // TODO: 添加decltype decay等以在静态断言中打印类型 template <bool asserted, typename unhandled_in_chain> void if_true() const { static_assert(asserted, "type X not handled in chain"); } }; struct no_option { no_option(std::function<void()> callback) : callback_func(std::move(callback)) {} void if_true() const { callback_func(); } std::function<void()> callback_func; }; template <typename... Ts> struct VariantHandler { using variant_t = std::variant<Ts...>; using optional_variant_t = std::optional < variant_t >; VariantHandler( const variant_t& from ) : variant_holder(from) {} VariantHandler( const optional_variant_t &from ) { if (from.has_value()) variant_holder = from; else variant_holder = std::nullopt; } optional_variant_t variant_holder; mutable std::unordered_set<std::size_t> processedTypes{}; template <typename T> const VariantHandler< Ts... > & operator | (const on_type<T>& type_handler) const { // FIXME 表达式无法在编译时求值 //static_assert(processedTypes.contains(typeid(T).hash_code()) == 0, "Duplicate type handler encountered"); processedTypes.insert(typeid(T).hash_code()); if (true == variant_holder.has_value()) { std::visit([&](const auto& value) { if constexpr (std::is_same_v<std::decay_t<decltype(value)>, T>) { type_handler.if_type(value); } }, variant_holder.value()); } return *this; // 或返回代理对象以支持后续链式调用 } const VariantHandler< Ts... > operator << (const no_option &on_true) const { if (false == variant_holder.has_value()) on_true.if_true(); return *this; } // 处理variant中未通过on_type链处理的所有剩余类型 //template <typename T> constexpr void operator | (const no_type& when_no_type) const { // TODO: 如何检查链中未注册的类型处理? when_no_type.if_true<true, bool>(); } // TODO: 若使用no_type作为链的最后参数,需确定遗漏处理的类型并触发断言 }; // TODO: 添加'requires'约束以校验variant类型,并对on_type<T>中T不属于Ts...的情况触发静态断言 template <typename... Ts> constexpr auto variants(const std::variant<Ts...>& variant) { return VariantHandler<Ts...>{variant}; } template <typename... Ts> constexpr auto variants(const std::optional<std::variant<Ts...>>& optional_variant) { return VariantHandler<Ts...>{optional_variant}; }
需要解决的核心问题
当前代码无法实现编译时的类型校验,需完成以下三点:
- 对
on_type<T>中的T进行校验:若T不属于目标std::variant的成员类型,触发静态断言 - 链式调用中禁止重复添加同一类型的处理:若多次调用
on_type<T>,第二次触发静态断言 - 当链末尾使用
no_type()时,检查是否处理了std::variant的所有成员类型:存在未处理类型则触发静态断言
内容的提问来源于stack exchange,提问作者Vitaly Protasov
相关产品推荐
相关产品推荐

