考虑
std::function
的声明:
template< class >
class function; /* undefined */
template< class R, class... Args >
class function<R(Args...)>;
我们有一个非特化的模板声明,它声明一个空结构,然后是 (partial?) 特化,但与我通常看到的部分特化不同,这个模板只使用在其模板参数中声明的类型(不'硬编码任何类型)。它的目的似乎是当
std::function
被实例化时:
std::function<float(int, int)>
然后
R
获取返回类型(float
)和(int, int)
进入参数包Args...
.
在练习实现我自己的记忆类时,我发现我必须提供与
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 { ... }
但这不对,
memoiser
必须实例化为 memoiser<float, int, int>
而不是 memoiser<float(int, int)>
,这是不合常理的,当通过 decltype
传入自由函数时不起作用(而 working版本)。
但是这里到底发生了什么?我知道第二个有效的部分专业化似乎需要将函数类型正确映射到模板参数,但是使用语言的什么确切特性来促进这一点?是否有可能以这种方式将更复杂构造的其他自定义精确映射干净地构造到模板参数中?:
为什么需要空的非专用模板声明?
template <class>
class triple;
template <class X, class Y, class Z>
class triple<std::tuple<X, Y, Z>> {}; // can use this to extract X,Y,Z from the tuple instantiated with
...
triple<std::tuple<int, float, char>> butternuts = {};
据我所知,这只是为了支持花哨或惯用的语法
std::function<float(int, int)>
,
并有效地防止其他可能的语法
std::function<float, int, int>
(因为我认为你不能从std::
中专门化一些东西)。
std::function
可能基于 Boost.Function,当时一些编译器被花哨的语法搞糊涂了。
所以 Boost.Function 提供了这两个选项,并调用了第二种“可移植”语法。
首选语法:
boost::function<float (int x, int y)> f;
可移植语法:
boost::function2<float, int, int> f;
看这里:https://www.boost.org/doc/libs/1_65_1/doc/html/function/tutorial.html#idp241211680
除了比较地道,可能当时这样实现起来也更容易;当您唯一的选择是手动对所有具有不同数量输入参数的案例进行编程时。 (当时还没有可变参数模板)
至于使用了语言的什么特性,我会说 1) 模板特化和 2) 函数在语言中具有明确定义的类型这一事实。
float(int, int)
是具体类型,可能是您实例化的类型,
但同样,float(*)(int, int)
也是一个具体类型,一个具体的指向函数的指针类型,或者 float(&)(int, int)
是一个引用函数类型。