C++成员函数首参类型推导疑问:取地址符作用与模板特化逻辑
MSVC C++20下成员函数指针的模板推导问题
问题代码
#include <tuple> template <typename... Args> struct args_holder { using type = std::tuple<Args...>; }; template <typename T> struct base_holder; template <typename Class, typename Proto, typename... Args> struct base_holder<Proto(Class::*)(Args...)> : args_holder<Args...> { }; template <typename T> using first_arg_type = std::tuple_element_t<0, typename base_holder<T>::type>; struct test_struct { void test_function(int x) { } }; int main() { static_assert(std::is_same_v<first_arg_type<decltype(test_struct::test_function)>, int>); }
编译报错信息:
'type': is not a member of any direct or indirect base class of 'base_holder<void (int)>'
在test_struct::test_function前添加&后代码可正常编译运行。
问题解答
问题1:&的具体作用是什么?
&在这里是显式获取成员函数的指针。虽然IntelliSense显示的decltype结果看起来一致,但MSVC编译阶段的处理严格遵循C++标准:
- 不加
&时,test_struct::test_function是成员函数的名字,decltype作用于它时,得到的是普通函数类型void(int),而非成员函数指针类型。 - 添加
&后,会显式生成成员函数指针类型void(test_struct::*)(int),这才匹配你定义的base_holder特化模板。
IntelliSense的提示做了简化,和实际编译阶段的类型推导结果有差异,这是导致混淆的原因。
问题2:typename base_holder<T>::type的推导流程
整个推导过程分三步:
- 特化模板匹配:当
T是成员函数指针类型(比如void(test_struct::*)(int))时,编译器会匹配base_holder的特化版本base_holder<Proto(Class::*)(Args...)>,并自动拆分这个复杂类型:Class被推导为test_struct(成员函数所属的类)Proto被推导为void(成员函数的返回值类型)Args...被推导为int(成员函数的参数列表)
- 继承获取类型别名:这个特化的
base_holder继承自args_holder<Args...>(也就是args_holder<int>),因此继承了args_holder中定义的type,即std::tuple<int>。 - 提取第一个参数类型:
first_arg_type通过std::tuple_element_t<0, ...>从std::tuple<int>中取出第一个元素的类型,最终得到int。
如果T是普通函数类型(不加&时的void(int)),编译器会匹配base_holder的主模板,而主模板没有定义type成员,因此触发编译错误。
内容的提问来源于stack exchange,提问作者Arush Agarampur
相关产品推荐
相关产品推荐

