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

如何在C++自定义模式匹配工具中实现switch同款fallthrough特性

实现思路

核心要对齐原生switch的fallthrough行为:

  • 优先找到第一个值匹配的分支,执行对应回调
  • 若回调返回Fallthrough类型,则跳过后续分支的值匹配校验,直接按定义顺序依次执行后续所有分支的回调
  • 直到某回调不返回Fallthrough(返回void或其他类型),终止执行
  • 无任何匹配分支返回false,有匹配分支返回true

关键修改点

  • 调整match方法的模板参数顺序,保证分支执行顺序和代码书写顺序一致
  • 新增辅助逻辑判断回调返回值类型,区分是否需要继续fallthrough
  • 重写invoke递归逻辑:先定位首个匹配分支,再从该分支开始逐次执行后续回调,按需穿透
  • 处理void返回值的回调默认终止穿透的逻辑

完整可运行代码

#include <iostream>
#include <tuple>
#include <optional>
#include <type_traits>
#include <utility>
#include <functional>

namespace test {

struct Fallthrough {};

namespace detail {
// 辅助判断返回值是否为Fallthrough
template<typename T>
constexpr bool is_fallthrough_v = std::is_same_v<std::decay_t<T>, Fallthrough>;

// 处理回调返回值,返回是否需要继续穿透
template<typename R>
bool handle_return(R&& rv) {
    if constexpr (is_fallthrough_v<R>) {
        return true;
    } else {
        return false;
    }
}
// 回调返回void的情况,默认不穿透
bool handle_return(void) {
    return false;
}

} // namespace detail

template <typename T, typename Fn> struct Match_pack {
    T  v;
    Fn fn;
};

// 递归终止:无剩余分支,返回是否已经匹配过
template <typename T>
bool invoke(T /*v*/, bool already_matched) {
    return already_matched;
}

template <typename T, typename Pack, typename... Tail>
bool invoke(T v, bool already_matched, Pack&& pack, Tail&&... tail) {
    bool need_exec = already_matched;
    // 还没匹配过的话先判断值是否匹配
    if (!already_matched && pack.v == v) {
        need_exec = true;
    }
    if (need_exec) {
        // 执行回调
        if constexpr (std::is_void_v<std::invoke_result_t<decltype(pack.fn)>>) {
            std::invoke(pack.fn);
            // 返回void,终止穿透
            return true;
        } else {
            auto rv = std::invoke(pack.fn);
            if (detail::handle_return(rv)) {
                // 需要穿透,继续执行后面的分支
                return invoke(v, true, std::forward<Tail>(tail)...);
            } else {
                // 不需要穿透,直接返回
                return true;
            }
        }
    }
    // 不需要执行当前分支,继续找下一个
    return invoke(v, false, std::forward<Tail>(tail)...);
}

template <typename...> struct Match_args;

template <typename... Args> struct Matcher {
    Matcher(Args... args) : vs_{args...}
    { }

    template <typename E, typename Fn> auto match(E e, Fn fn)
        -> Match_args<Matcher<Args...>, Match_pack<E, Fn>>
    {
        return {std::move(*this), {e, fn}};
    }

protected:
    std::tuple<Args...> vs_;
};

template <typename... Args, typename... Tail>
struct Match_args<Matcher<Args...>, Tail...> : Matcher<Args...>, Tail... {
    Match_args(Matcher<Args...>&& args, Tail&&... tail)
        : Matcher<Args...>{std::move(args)}
        , Tail(std::move(tail))...
    { }

    template <typename E, typename Fn> auto match(E e, Fn fn) 
        // 新增分支放到参数末尾,保证执行顺序和书写顺序一致
        -> Match_args<Matcher<Args...>, Tail..., Match_pack<E, Fn>>
    {
        return {std::move(*this), std::forward<Tail>(*this)..., {e, fn}};
    }

    operator bool() {
        return std::apply([&](auto... vs) {
            return (invoke(vs, false, static_cast<Tail&>(*this)...) && ...);
        }, this->vs_);
    }
};

} // namespace test


enum {
    SQLITE_ERROR,
    SQLITE_MISUSE,
    SQLITE_CONSTRAINT,
    SQLITE_DONE,
    SQLITE_ROW,
    SQLITE_BUSY
};

auto step() {
    return SQLITE_MISUSE;   
}

template <typename... Args>
auto matcher(Args... args) {
    return test::Matcher{args...}; 
}

std::optional<int> foo() {
    auto errcode = step();
    if (SQLITE_DONE == errcode) return std::nullopt;
    if (SQLITE_ROW  == errcode) return 1;

    bool f = matcher(errcode)
        .match(SQLITE_MISUSE    , [] { std::cout << "SQLITE_MISUSE\n";     return test::Fallthrough{}; })
        .match(SQLITE_CONSTRAINT, [] { std::cout << "SQLITE_CONSTRAINT\n"; return test::Fallthrough{}; })
        .match(SQLITE_ERROR     , [] { std::cout << "SQLITE_ERROR\n"; throw std::runtime_error("XXX"); })
        .match(SQLITE_BUSY      , [] { std::cout << "SQLITE_BUSY\n" ; })
        ;

    std::cout << f;
    return 2;
}

int main() {
    try {
        auto x = foo();
    } catch(const std::runtime_error& e) {
        std::cout << "catch exception: " << e.what() << "\n";
    }
}

运行效果说明

当step()返回SQLITE_MISUSE时,输出顺序为:

SQLITE_MISUSE
SQLITE_CONSTRAINT
SQLITE_ERROR
catch exception: XXX

完全符合预期的fallthrough行为,和原生switch写了三个连续fallthrough的case效果一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 05:24:05