能否通过单一表达式调用std::variant中类型的同名同签名方法?
问题解答
首先明确:直接写v->f()是不行的,因为std::variant本身没有重载->运算符,也没有名为f()的成员函数。但我们可以通过封装辅助逻辑,实现类似的单一表达式调用,且不需要用到std::visit。
针对双类型variant的极简写法
如果你的std::variant只有A和B两种类型,可以直接用三元运算符结合index()和std::get实现单一表达式调用:
(v.index() == 0 ? std::get<A>(v).f() : std::get<B>(v).f())
这个表达式会根据v当前激活的类型,调用对应对象的f()方法,完全不需要std::visit。
通用封装方案(支持任意多类型variant)
如果需要支持更多类型的std::variant,可以封装辅助逻辑模拟->的调用形式:
方法1:封装辅助函数
写一个模板函数,通过switch-case根据variant的索引分发调用:
#include <variant> #include <stdexcept> struct A { void f() {} }; struct B { void f() {} }; template<typename... Ts> void call_f(std::variant<Ts...>& v) { switch(v.index()) { case 0: std::get<0>(v).f(); break; case 1: std::get<1>(v).f(); break; // 新增类型时继续添加对应的case分支 default: throw std::bad_variant_access{}; } } // 调用方式(单一表达式): // call_f(v);
方法2:代理类模拟->调用
如果想要更贴近v->f()的写法,可以实现一个代理类,重载->运算符:
#include <variant> #include <stdexcept> #include <tuple> struct A { void f() {} }; struct B { void f() {} }; template<typename... Ts> struct VariantProxy { std::variant<Ts...>& var; template<typename T> constexpr bool holds_type() const { return var.index() == std::variant<Ts...>::template index_of<T>(); } auto operator->() { if constexpr (sizeof...(Ts) >= 1) { using FirstType = std::tuple_element_t<0, std::tuple<Ts...>>; if (holds_type<FirstType>()) { return &std::get<FirstType>(var); } } if constexpr (sizeof...(Ts) >= 2) { using SecondType = std::tuple_element_t<1, std::tuple<Ts...>>; if (holds_type<SecondType>()) { return &std::get<SecondType>(var); } } // 新增类型时继续添加对应的判断分支 throw std::bad_variant_access{}; } }; template<typename... Ts> constexpr auto make_variant_proxy(std::variant<Ts...>& v) { return VariantProxy<Ts...>{v}; } // 调用方式(类似v->f()的单一表达式): // make_variant_proxy(v)->f();
需要注意的是,这两种方案都需要在新增variant类型时手动扩展分支,不像std::visit能自动适配所有类型,但确实满足了「不用std::visit」和「单一表达式调用」的要求。
内容的提问来源于stack exchange,提问作者hopeless-programmer
相关产品推荐
相关产品推荐

