为何std::function采用空模板声明+偏特化的实现方式?
先看std::function的声明:
template< class > class function; /* undefined */ template< class R, class... Args > class function<R(Args...)>;
该形式包含未定义的非特化模板声明,以及一个偏特化版本。此偏特化未硬编码类型,仅使用模板参数中声明的类型,目的是在实例化std::function<float(int, int)>时,将返回类型float绑定到R,参数列表(int, int)绑定到参数包Args...。
在实现自定义记忆化类memoiser时,必须采用与std::function完全相同的模板接口才能正常工作。该类包装std::function对象,需获取返回类型和参数以通过函数调用运算符转发调用:
template <class> class memoiser; template <class R, class... Args> class memoiser<R(Args...)> { memoiser(std::function<R(Args...)>); R operator()(Args... args); };
如果移除第一个声明会导致第二个无法编译;若不采用这种方式,只能写成:
template <class R, class... Args> class memoiser { ... };
但这种方式不符合C++惯用写法:实例化时需写成memoiser<float, int, int>而非memoiser<float(int, int)>,且无法直接用decltype传入自由函数(前者则可以)。
问题解答
1. 这种实现具体利用了C++的哪项语言特性?
这是利用了类模板的偏特化特性,更具体地说是针对函数类型的模板偏特化。C++允许为类模板定义匹配特定模板参数规则的特化版本,这里的偏特化专门匹配R(Args...)这种函数类型签名,将函数的返回值和参数列表拆解为独立的模板参数R和Args...。
2. 为何必须要有空的非特化模板声明?
模板偏特化的定义必须依赖已声明的主模板(非特化模板)。C++语法要求:不能直接定义偏特化版本,必须先声明最通用的主模板(哪怕未定义),告知编译器“这是一个类模板,后续会有针对特定场景的特化”。如果没有这个主模板声明,编译器会把memoiser<R(Args...)>当成全新的类模板定义,而非某个模板的特化,因此会编译失败。
3. 是否可以用类似方式将更复杂的结构清晰映射到模板参数中?
完全可以。这种“用偏特化拆解复杂类型”的技巧在C++元编程中非常常用:
- 可拆解指针、引用类型,比如针对
T*或T&的偏特化,提取原始类型T; - 可拆解容器类型,比如针对
std::vector<T>的偏特化,提取元素类型T; - 甚至可拆解带CV限定符的成员函数类型
R(T::*)(Args...) const,将成员函数的所属类、返回值、参数、CV属性拆解为独立模板参数。
这种方式能让模板使用更符合直觉,比如直接用memoiser<decltype(my_function)>实例化,无需手动拆分返回值和参数。
内容的提问来源于stack exchange,提问作者saxbophone

