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

如何向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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:29:59