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

使用std::visit重载模式时引用传递struct A编译失败的问题

问题

尝试使用重载模式结合std::visit处理std::variant,但在以引用方式处理自定义结构体A<int>时出现编译错误。测试代码如下:

#include <variant>
#include <iostream>

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

  consteval void operator()(auto&) const
  {
    static_assert(false, "ERROR");
  }
};

template<typename T, typename...>
struct A {};

int main()
{
  const std::variant<int, bool, A<int>> v{true};

  std::visit(overload{
                 [](A<int> val) { /* works */ },
                 //[](A<int>& val) { /* does not work */ },
                 [](int val) { std::cout << val; },
                 [](bool val) { std::cout << std::boolalpha << val; },
             },
             v);
}

编译错误信息(GCC 11环境):

error: static assertion failed: ERROR
11 | static_assert(false, "ERROR");
| ^~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:61:14: note: in instantiation of function template specialization 'overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)>::operator()' requested here
56 | { return std::forward<_Fn>(__f)(std::forward<_Args>(__args)...); }
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/invoke.h:96:19: note: in instantiation of function template specialization 'std::__invoke_impl<void, overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)>, const A &>' requested here
91 | return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn),
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:1016:16: note: in instantiation of function template specialization 'std::__invoke<overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)>, const A &>' requested here
1011 | return std::__invoke(std::forward<_Visitor>(__visitor),
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:1032:14: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void ()(overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)> &&, const std::variant<int, bool, A> &)>, std::integer_sequence<unsigned long, 2>>>::__visit_invoke' requested here
1027 | decltype(__visit_invoke(std::declval<_Visitor>(),
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:968:48: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void (
)(overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)> &&, const std::variant<int, bool, A> &)>, std::integer_sequence<unsigned long, 2>>>::_S_apply' requested here
963 | std::index_sequence<__indices..., __index>>::_S_apply();
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:947:7: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
942 | (_S_apply_single_alt<false, __var_indices>(
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:932:2: note: in instantiation of function template specialization 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void ()(overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)> &&, const std::variant<int, bool, A> &), 3>, std::integer_sequence>>::_S_apply_all_alts<0UL, 1UL, 2UL>' requested here
927 | _S_apply_all_alts(
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:1055:59: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void (
)(overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)> &&, const std::variant<int, bool, A> &), 3>, std::integer_sequence>>::_S_apply' requested here
1050 | = __gen_vtable_impl<_Array_type, std::index_sequence<>>::_S_apply();
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:1724:45: note: in instantiation of static data member 'std::__detail::__variant::__gen_vtable<std::__detail::__variant::__deduce_visit_result<void, overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)> &&, const std::variant<int, bool, A> &>>::_S_vtable' requested here
1719 | _Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
| ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/variant:1761:18: note: in instantiation of function template specialization 'std::__do_visit<std::__detail::__variant::__deduce_visit_result<void, overload<(lambda at prog_joined.cpp:28:18), (lambda at prog_joined.cpp:30:18), (lambda at prog_joined.cpp:31:18)>, const std::variant<int, bool, A> &>>' requested here
1756 | return std::__do_visit<_Tag>(
| ^
Line 22: Char 8: note: in instantiation of function template specialization 'std::visit<overload<(lambda at solution.cpp:28:18), (lambda at solution.cpp:30:18), (lambda at solution.cpp:31:18)>, const std::variant<int, bool, A> &>' requested here
22 | std::visit(overload{
| ^

错误原因分析
  1. const引用不匹配:代码中的std::variant是const类型,std::visit访问其内部值时会传递const A<int>&类型的参数。但启用的lambda是[](A<int>& val),无法绑定到const引用,导致重载决议失败。
  2. 兜底operator()被实例化:当没有匹配的lambda时,编译器会尝试匹配overload结构体中的兜底模板operator()(auto&)。由于static_assert(false)是无条件触发的,无论是否实际调用该函数,只要模板被实例化就会触发编译错误。GCC 11在处理std::visit的vtable生成时,会对variant的所有可能类型进行重载决议检查,即使当前variant存储的是bool,仍然会尝试为A<int>类型匹配visitor,最终实例化了兜底的operator()导致错误。
解决方案

针对GCC 11环境,有两种可行的修改方式:

方式一:修改lambda参数为const引用

将处理A<int>的lambda参数改为const A<int>&,匹配const variant传递的参数类型:

std::visit(overload{
               [](const A<int>& val) { /* 现在可以正常工作 */ },
               [](int val) { std::cout << val; },
               [](bool val) { std::cout << std::boolalpha << val; },
           },
           v);

方式二:改进兜底operator()的SFINAE约束

让兜底的operator()仅在没有其他匹配重载时才参与决议,避免被不必要地实例化。可以通过requires约束实现:

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

  template<typename T>
  consteval void operator()(T&&) const requires (!std::is_invocable_v<const overload&, T&&>)
  {
    static_assert(false, "ERROR");
  }
};

这样只有当其他lambda都无法匹配参数类型时,兜底的operator()才会被考虑,避免在std::visit的vtable生成阶段被实例化触发错误。如果需要处理非const的variant,也可以保留原有的引用参数,调整约束即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:32:32