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

传入派生类成员函数指针为模板参数时,如何捕获其所属类类型?

解决派生类成员函数指针作为模板参数时的类型推导问题

嘿,这个坑我之前踩过!你遇到的问题本质是C++的隐式转换在搞鬼:派生类继承来的成员函数指针,可以自动转换成基类的成员函数指针,而模板参数推导会优先选择这个转换后的类型,所以才会输出CObject而不是CDerivedObject。

先还原一下你的场景(我猜你的代码大概是这样的):

#include <iostream>
#include <typeinfo>

class CObject {
public:
    void setID(int) {}
};

class CDerivedObject : public CObject {};

template<typename _T>
void function_template_test(void (_T::*func)(int)) {
    std::cout << typeid(_T).name() << std::endl;
}

int main() {
    function_template_test(&CDerivedObject::setID);
    return 0;
}

编译运行后输出7CObject,完全不符合预期对吧?

核心原因

&CDerivedObject::setID的原始类型是void (CDerivedObject::*)(int),但因为setID是从CObject继承来的,这个指针可以隐式转换成void (CObject::*)(int)。模板推导的时候,编译器会优先选择这个转换后的类型来匹配模板参数_T,所以就得到了基类类型。

解决方案:捕获原始成员函数指针类型

要拿到派生类类型,我们得绕过这个隐式转换,直接处理原始的成员函数指针类型,再从中提取所属的类。这里有两种实用的方法:

方法1:自定义类型萃取工具(C++11及以上可用)

我们可以写一个模板特化的类型萃取器,专门从成员函数指针里提取所属的类:

#include <iostream>
#include <typeinfo>

class CObject {
public:
    void setID(int) {}
};

class CDerivedObject : public CObject {};

// 类型萃取模板:提取成员函数指针的所属类
template<typename MemFunc>
struct member_function_class;

// 特化版本:匹配非const成员函数指针
template<typename T, typename Ret, typename... Args>
struct member_function_class<Ret (T::*)(Args...)> {
    using type = T;
};

// 方便使用的别名
template<typename MemFunc>
using member_function_class_t = typename member_function_class<MemFunc>::type;

// 修改后的模板:直接接收成员函数指针类型
template<typename MemFunc>
void function_template_test(MemFunc func) {
    using DerivedType = member_function_class_t<MemFunc>;
    std::cout << typeid(DerivedType).name() << std::endl;
}

int main() {
    function_template_test(&CDerivedObject::setID);
    return 0;
}

现在编译运行,就会输出派生类的类型(比如14CDerivedObject,具体输出取决于编译器的name mangling规则)。

方法2:C++17的auto模板参数(更简洁)

如果你的编译器支持C++17,还可以用auto模板参数直接捕获成员函数指针,再搭配萃取工具:

#include <iostream>
#include <typeinfo>

class CObject {
public:
    void setID(int) {}
};

class CDerivedObject : public CObject {};

// 复用上面的类型萃取器
template<typename MemFunc>
struct member_function_class;

template<typename T, typename Ret, typename... Args>
struct member_function_class<Ret (T::*)(Args...)> {
    using type = T;
};

template<typename MemFunc>
using member_function_class_t = typename member_function_class<MemFunc>::type;

// 用auto模板参数直接接收成员函数指针
template<auto MemFunc>
void function_template_test() {
    using DerivedType = member_function_class_t<decltype(MemFunc)>;
    std::cout << typeid(DerivedType).name() << std::endl;
}

int main() {
    // 调用时直接把成员函数指针作为模板参数传入
    function_template_test<&CDerivedObject::setID>();
    return 0;
}

这种写法更直观,直接把成员函数指针固定为模板参数,完全避免了隐式转换的问题。

总结

关键就是不要让模板推导去匹配基类的成员函数指针类型,而是直接捕获原始的派生类成员函数指针,再通过类型萃取提取出所属的类类型,这样就能得到你想要的CDerivedObject了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:30:39