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

能否用std::visit处理包含重复类型的std::variant?

按索引用std::visit处理含重复类型的std::variant

你当前的Token定义中,std::variant<int, std::string, std::string>包含两个相同类型的变体,仅靠索引区分数字、关键字、变量。常规std::visit因类型重复无法直接区分分支,但可以通过绑定索引信息的方式实现按索引调用处理函数,无需额外包装类,以下是两种可行方案:

方案一:带索引标签的访问器

通过自定义visit_by_index函数,将变体的索引包装为std::integral_constant传递给访问器,利用编译期constexpr if匹配索引并调用对应处理函数:

#include <variant>
#include <string>
#include <iostream>
#include <type_traits>

enum TokenType : std::size_t {
    Number = 0, Keyword = 1, Variable = 2,
};
using Token = std::variant<int, std::string, std::string>;

// 示例处理函数
void do_sth_with_number(int num) {
    std::cout << "Number: " << num << "\n";
}
void do_sth_with_keyword(const std::string& kw) {
    std::cout << "Keyword: " << kw << "\n";
}
void do_sth_with_variable(const std::string& var) {
    std::cout << "Variable: " << var << "\n";
}

// 接收索引标签和对应值的访问器
struct TokenVisitor {
    template<std::size_t I>
    void operator()(std::integral_constant<std::size_t, I>, const typename std::variant_alternative<I, Token>::type& val) const {
        if constexpr (I == TokenType::Number) {
            do_sth_with_number(val);
        } else if constexpr (I == TokenType::Keyword) {
            do_sth_with_keyword(val);
        } else if constexpr (I == TokenType::Variable) {
            do_sth_with_variable(val);
        }
    }
};

// 包装std::visit,传递索引标签
template<typename Variant, typename Visitor>
void visit_by_index(Variant&& var, Visitor&& visitor) {
    std::visit(
        [&](auto&& val) {
            using VariantType = std::decay_t<Variant>;
            using ValType = std::decay_t<decltype(val)>;
            constexpr std::size_t Index = std::variant_index<VariantType, ValType>::value;
            visitor(std::integral_constant<std::size_t, Index>{}, std::forward<decltype(val)>(val));
        },
        std::forward<Variant>(var)
    );
}

int main() {
    Token num_token(std::in_place_index<TokenType::Number>, 42);
    Token kw_token(std::in_place_index<TokenType::Keyword>, "if");
    Token var_token(std::in_place_index<TokenType::Variable>, "x");

    visit_by_index(num_token, TokenVisitor{}); // 输出:Number: 42
    visit_by_index(kw_token, TokenVisitor{}); // 输出:Keyword: if
    visit_by_index(var_token, TokenVisitor{}); // 输出:Variable: x
    return 0;
}

方案二:利用std::index_sequence遍历索引

通过std::apply和std::index_sequence遍历变体的所有可能索引,匹配当前变体的索引后直接调用对应处理函数,写法更紧凑:

#include <variant>
#include <string>
#include <iostream>
#include <utility>

enum TokenType : std::size_t {
    Number = 0, Keyword = 1, Variable = 2,
};
using Token = std::variant<int, std::string, std::string>;

void do_sth_with_number(int num) { std::cout << "Number: " << num << "\n"; }
void do_sth_with_keyword(const std::string& kw) { std::cout << "Keyword: " << kw << "\n"; }
void do_sth_with_variable(const std::string& var) { std::cout << "Variable: " << var << "\n"; }

int main() {
    Token var_token(std::in_place_index<TokenType::Variable>, "x");
    Token kw_token(std::in_place_index<TokenType::Keyword>, "return");
    Token num_token(std::in_place_index<TokenType::Number>, 100);

    auto visit_token = [](const Token& token) {
        // 遍历所有可能的索引
        std::apply(
            [&](auto... Is) {
                ( [&]() {
                    if (token.index() == Is) {
                        if constexpr (Is == TokenType::Number) {
                            do_sth_with_number(std::get<Is>(token));
                        } else if constexpr (Is == TokenType::Keyword) {
                            do_sth_with_keyword(std::get<Is>(token));
                        } else if constexpr (Is == TokenType::Variable) {
                            do_sth_with_variable(std::get<Is>(token));
                        }
                    }
                }(), ... );
            },
            std::make_index_sequence<std::variant_size_v<Token>>{}
        );
    };

    visit_token(num_token); // 输出:Number: 100
    visit_token(kw_token); // 输出:Keyword: return
    visit_token(var_token); // 输出:Variable: x
    return 0;
}

两种方案均无需修改原Token的定义,完全按照索引分支处理逻辑,和你原来的switch实现效果一致,且保留了std::visit的编译期检查优势。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 14:32:35