如何向visit函数传递成员函数指针,从variant对象调用类成员函数?
能否将成员函数指针传递给variant的visit函数?
答案是可以,但你当前的代码无法通过编译——直接写callSomeF(f1)时,编译器无法识别f1是C1还是C2的成员函数。下面是几种可行的实现方式,帮你实现“根据variant存储的类型调用对应类的同名成员函数”的需求:
方案1:显式传递带类域的成员函数指针
这种方式最直接,利用std::invoke统一处理成员函数的调用,只要传递的成员函数指针与variant当前存储的类型匹配即可:
#include <variant> #include <functional> struct C1 { bool f1() { return true; } bool f2() { return false; } bool f3() { return true; } }; struct C2 { bool f1() { return false; } bool f2() { return true; } bool f3() { return false; } }; std::variant<C1, C2> C; template<typename MemFn> bool callSomeF(MemFn Fn) { return std::visit( [&](auto &node){ return std::invoke(Fn, node); }, C); } int main() { C = C1{}; callSomeF(&C1::f1); // 调用C1::f1 callSomeF(&C1::f2); // 调用C1::f2 C = C2{}; callSomeF(&C2::f1); // 调用C2::f1 callSomeF(&C2::f2); // 调用C2::f2 }
注意:如果variant存储的是C1,却传递&C2::f1会编译报错,因为无法用C2的成员函数指针调用C1对象。
方案2:用模板参数简化调用(接近你的期望写法)
如果你想避免每次写类域,可以通过模板参数指定成员函数,搭配宏进一步简化调用:
#include <variant> #include <type_traits> struct C1 { bool f1() { return true; } bool f2() { return false; } bool f3() { return true; } }; struct C2 { bool f1() { return false; } bool f2() { return true; } bool f3() { return false; } }; std::variant<C1, C2> C; template<typename T, bool (T::*MemFn)()> bool callSomeF() { return std::visit( [](auto &node) -> bool { // 匹配variant中的实际类型,调用对应成员函数 if constexpr (std::is_same_v<std::decay_t<decltype(node)>, T>) { return (node.*MemFn)(); } else { return (node.*MemFn)(); } }, C); } // 可选:用宏简化调用,省去类域书写 #define CALL_FN(Fn) callSomeF<C1, &C1::Fn>() int main() { C = C1{}; callSomeF<C1, &C1::f1>(); // 调用C1::f1 CALL_FN(f2); // 宏简化调用,效果同上 C = C2{}; callSomeF<C2, &C2::f1>(); // 调用C2::f1 }
方案3:重载函数对象(最灵活)
定义重载的函数对象,让std::visit自动匹配variant中的类型并调用对应成员函数,也可以直接用lambda实现:
#include <variant> struct C1 { bool f1() { return true; } bool f2() { return false; } bool f3() { return true; } }; struct C2 { bool f1() { return false; } bool f2() { return true; } bool f3() { return false; } }; std::variant<C1, C2> C; // 封装调用f1的重载对象 struct CallF1 { bool operator()(C1& c) const { return c.f1(); } bool operator()(C2& c) const { return c.f1(); } }; // 同理可定义CallF2、CallF3... bool callSomeF(auto visitor) { return std::visit(visitor, C); } int main() { C = C1{}; callSomeF(CallF1{}); // 调用对应类型的f1 // 直接用lambda更简洁 callSomeF([](auto& node){ return node.f2(); }); }
核心要点
std::invoke(C++17+)可以统一处理普通函数、成员函数指针、函数对象的调用,是std::visit中调用成员函数的理想工具。- 不同类的同名成员函数指针类型不同,因此模板推导或函数重载是处理这类场景的核心手段。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

