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

使用重载模式调用std::visit失败,求排查嵌套variant分发问题

问题描述

尝试用访问器重载模式处理嵌套的std::variant时,编译报错:

error: no matching function for call to 'visit ...'

对应的代码如下:

#include <variant>
#include <cstdio>

template <typename... Ts>
struct overload : Ts...
{
    using Ts::operator() ...;
};

int main() {

    struct StateA{};
    struct StateB{};
    struct StateC{};

    using state_type = std::variant<StateA, StateB, StateC>;
    auto state = state_type{StateA{}};

    struct EventA{};
    struct EventB{};

    struct WifiConnectedNote{};
    struct ErrorNote{};

    using QueueVariantType = std::variant<std::monostate, WifiConnectedNote, ErrorNote>;
    
    using event_type = std::variant<std::monostate, QueueVariantType, EventA, EventB>;
    auto event = event_type{EventA{}};

    for (size_t i=0; i < 10; i++) {
        state = std::visit(overload{
            [&](QueueVariantType&& variant, auto&& state_tmp) -> state_type {
                std::visit(overload{
                    [&](WifiConnectedNote&&, auto&&){
                        printf("WifiConnectedNote A\n");
                    },
                    [&](ErrorNote&&, auto&&){
                        printf("ErrorNote A\n");
                    },
                    [&](auto&&, auto&&){
                        printf("Other in Queue variant\n");
                    },
                }, std::forward<decltype(variant)>(variant), std::forward<decltype(state_tmp)>(state_tmp));
                return StateC{};
            },
            [&](auto&&, const StateA&) -> state_type {
                printf("State A\n");
                return StateC{};
            },
            [&](auto&&, const StateB&) -> state_type {
                printf("State B\n");
                return StateA{};
            },
            [&](auto&&, const StateC&) -> state_type {
                printf("State C");
                return StateB{};
            },
        }, std::move(event), state);
    }
}
问题原因及修复方案

编译失败主要是两个核心问题:

1. 参数值类别与引用类型不匹配

外层std::visit里用std::move(event)将event转为右值,但给QueueVariantType的处理函数签名是QueueVariantType&&——可event_type内部存储的QueueVariantType是左值类型,导致无法匹配。同时其他处理函数的第二个参数用了const StateA&这类常量引用,和state的左值传递结合后,重载决议找不到合适的匹配项。

2. 嵌套std::visit参数传递错误

内层std::visit只需要访问QueueVariantType这一个变体,但你传了两个参数(variant和state_tmp),而内层访问器的每个lambda都接收两个参数。std::visit若接收多个参数,要求所有参数都是std::variant类型,但state_tmp是具体的状态类型(不是变体),直接引发匹配错误。

修复后的代码

#include <variant>
#include <cstdio>

template <typename... Ts>
struct overload : Ts...
{
    using Ts::operator() ...;
};

int main() {

    struct StateA{};
    struct StateB{};
    struct StateC{};

    using state_type = std::variant<StateA, StateB, StateC>;
    auto state = state_type{StateA{}};

    struct EventA{};
    struct EventB{};

    struct WifiConnectedNote{};
    struct ErrorNote{};

    using QueueVariantType = std::variant<std::monostate, WifiConnectedNote, ErrorNote>;
    
    using event_type = std::variant<std::monostate, QueueVariantType, EventA, EventB>;
    auto event = event_type{EventA{}};

    for (size_t i=0; i < 10; i++) {
        state = std::visit(overload{
            // 修改为左值引用,适配variant内部存储的对象类型
            [&](QueueVariantType& variant, auto& state_tmp) -> state_type {
                // 内层visit只处理QueueVariantType,state_tmp直接从捕获获取即可
                std::visit(overload{
                    [&](WifiConnectedNote){
                        printf("WifiConnectedNote A\n");
                    },
                    [&](ErrorNote){
                        printf("ErrorNote A\n");
                    },
                    [&](std::monostate){
                        printf("Other in Queue variant\n");
                    },
                }, variant);
                return StateC{};
            },
            // 统一参数引用类型,去掉不必要的const(也可保留const,只要保持一致)
            [&](auto&, StateA&) -> state_type {
                printf("State A\n");
                return StateC{};
            },
            [&](auto&, StateB&) -> state_type {
                printf("State B\n");
                return StateA{};
            },
            [&](auto&, StateC&) -> state_type {
                printf("State C\n");
                return StateB{};
            },
        }, event, state); // 除非后续不再使用event,否则不需要std::move
    }
}

额外说明

  • 如果确实需要转移event的所有权,可以把外层访问器的第一个参数改成auto&&适配右值,或者为每种事件类型单独处理右值版本,但一般直接传递左值引用更安全。
  • 内层std::visit如果需要用到state_tmp,直接通过lambda的捕获获取即可,无需作为std::visit的参数传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 15:27:57