函数模板的类型参数(即模板实参)可以推导,不需要显示的写出来;但是类模板的类型参数必须显示的写出来。
函数模板是不能偏特化的(可以全特化),这是很神奇的地方,貌似在C++11中仍然是这样。不过由于函数模板可以重载,一样可以达到偏特化的效果;实际上,函数模板偏特化和重载的界线非常模糊。
以下是全特化的例子:
template<typename T1, typename T2>
T1 f(T1 a, T2 b)
{
return a * b;
}
template<>
int f<int, int> (int a, int b)
{
return a * b;
}
但是,如果是偏特化,就报错了:
template<typename T>
T f<T, int> (T a, int b)
{
return a * b;
}
由于函数模板的类型参数在实例化时可以推导,假若视作把函数模板id的类型拿掉:
template<typename T>
T f (T a, int b)
{
return a * b;
}
这样就可以,此时,就变成了函数模板的重载。。。。。。 有意思吧,实际上,跟偏特化多么像哇(特化有两个<>括号,template后一个,名称后一个--和名称一起构成模板id)。所以说函数模板不能偏特化也没有什么,完全可以被重载取代。
自己利用可变参数模板实现的一个有std::function 部分功能的类:
template<typename Signature>
class myfunction
{
};
template<typename R, typename... Args>
class myfunction<R (Args...)>
{
typedef R (*Ptr)(Args...);
Ptr m_p;
public:
myfunction(Ptr p) : m_p(p)
{
}
R operator()(Args... args)
{
return m_p(args...);
}
};
template<typename R, typename T, typename ... Args>
class myfunction<R(T*, Args...)>
{
typedef R (T::* mPtr)(Args...);
mPtr m_p;
public:
myfunction(mPtr p): m_p(p)
{
}
R operator()(T* likeThis, Args...args)
{
return (likeThis->*m_p)(args...);
}
};
template<typename R, typename T, typename ... Args>
class myfunction<R(T, Args...)>
{
typedef R (T::* mPtr)(Args...);
mPtr m_p;
public:
myfunction(mPtr p): m_p(p)
{
}
R operator()(T likeThis, Args...args)
{
return (likeThis.*m_p)(args...);
}
};
template<typename R, typename T, typename ... Args>
class myfunction<R(T&, Args...)>
{
typedef R (T::* mPtr)(Args...);
mPtr m_p;
public:
myfunction(mPtr p): m_p(p)
{
}
R operator()(T& likeThis, Args...args)
{
return (likeThis.*m_p)(args...);
}
};
可以包装普通函数跟成员函数,标准库中的function,包装成员函数时,第一个参数可以是普通传值类型,也可以是指针类型,也可以是引用类型。
关于可变参数模板的参数包:打包的过程是将 ... 省略号的左右由逗号分离的一个模式打包一起;解包是按 ... 省略号前边的模式匹配着展开。
需要注意的是,后边的function<> 是前边的一个特化,虽然这里模板的类型参数的个数不相同。所以我的总结是,无论类模板还是函数模板,只要有两个尖括号<>, 一个在template 后,一个在模板id中,都是基本模板的一个特化版本,而不管模板的类型参数的个数,但是最终表达的类型的个数必须相同。主模板只有一个模板参数,特化版本虽然有多个,但是最终表达的仍然只能是一个复合的类型,而不能是多个。
函数模板的类型参数在实例化是可以推导,但是全特化(函数模板不支持偏特化),仍然需要写全函数模板的模板id,因为此时是定义。
下边是对 tuple 的解释:代码在这里
template <class... Ts> struct tuple {}; //这里是基本模板,实际上...模板参数包可以表示0个以上的类型参数
//所以这里是用来表示 0 个模板参数 用来结束递归
template <class T, class... Ts> //如果另外有只需要一个模板参数的版本 会与T, ...Ts 版本冲突 因为Ts 可以表示没有
struct tuple<T, Ts...> : tuple<Ts...> {
tuple(T t, Ts... ts) : tuple<Ts...>(ts...), head(t) {} //注意这里的包扩展 最后扩展成tuple<E1, E2, E3>(e1,e2,e3) 分两步展开 先展开前边的包 再后边的
T head; //展开之后 每一层里都有个独立的 head 成员
};
template <size_t, class> struct elem_type_holder; //辅助模板 用来获取类型 注意这里的基本后边模板只是用到声明 所以无需定义
template <class T, class... Ts>
struct elem_type_holder<0, tuple<T, Ts...>> { //名称相同的 带template后一个尖括号<> 名称后一个尖括号<> 都是基本模板的偏特化
typedef T type;
};
template <size_t k, class T, class... Ts>
struct elem_type_holder<k, tuple<T, Ts...>> { //注意这里每次剥离tuple 类型参数中的前边一个 通过k来计数控制 相当于在递归中使用计数来控制递归的层数
typedef typename elem_type_holder<k - 1, tuple<Ts...>>::type type; //从k-1 一直到 0 一共进行了k次 如此获得参数包第k个类型参数(最开始的T不在参数包中)
};
template <size_t k, class... Ts>
typename std::enable_if< //enable_if在其第一个条件为真时 其成员类型才有效 否则会发生编译错误
k == 0, typename elem_type_holder<0, tuple<Ts...>>::type&>::type
get(tuple<Ts...>& t) {
return t.head;
}
template <size_t k, class T, class... Ts>
typename std::enable_if<
k != 0, typename elem_type_holder<k, tuple<T, Ts...>>::type&>::type //注意这里返回值是引用
get(tuple<T, Ts...>& t) { //这里也通过丢掉第一个类型参数 达到递归的效果
tuple<Ts...>& base = t; //还有这里基类引用指向的派生类
return get<k - 1>(base); //一直这么剥离下去 直到能匹配上边一个重载版本
}