目录
一、Variadic Templates(可变参数模板)概述
2. 使用variadic templates 重写printf()
4. 参数类型相同,递归调用标准库的std::max()实现maximum()
一、Variadic Templates(可变参数模板)概述
1、谈的是模板Templates:
-
函数模板
-
类模板
2、变化的是模板参数:
-
参数个数:利用参数个数逐一递减的特性,实现递归函数的调用,使用函数模板完成。
-
参数类型:利用参数个数逐一递减以致参数类型也逐一递减的特性,实现递归继承或递归复合,以类模板完成。
3、print应用举例
void print()
{
}
template <typename T, typename... Types> //这里的...是关键字的一部分:模板参数包
void print(const T& firstArg, const Types&... args) //这里的...要写在自定义类型Types后面:函数参数类型包
{
cout << firstArg << endl;
print(args...); //这里的...要写在变量args后面:函数参数包
}
- 1. 注意三种不同的...的应用环境,这些都是语法规则,所以记住即可;...就是一个所谓的pack(包)。
- 2. 还要注意的是,在可变模板参数内部可以使用sizeof...(args)得到实参的个数。
- 3. 如果同时定义了void print(const Types&... args):
template <typename... Types>
void print(const Types&... args)
{/*......*/}
那么这两个函数是否可以并存,如果可以那么这两个定义会优先调用哪个?换句话说,哪个定义更加泛化,哪个更加特化?这个侯捷老师说以后会做解释。
(更新:这个void print(const Types&... args)定义更加泛化,所以会调用之前定义的更特化的版本void print(const T& firstArg, const Types&... args))
二、Variadic Templates的两个应用
1. 实现函数的递归调用
举了一个unordered容器中hash函数的计算例子:万用的哈希函数,函数入口return hash_val(c.fname, c.lname, c.no);
class CustomerHash{
public:
std::size_t operator() (const Customer& c) const {
// 万用的哈希函数,函数入口return hash_val(c.fname, c.lname, c.no);
return hash_val(c.fname, c.lname, c.no);
}
};
template <typename T, typename... Types>
inline void hash_val(size_t& seed, const T& val, const Types&... args){
hash_combine(seed, val);
hash_val(seed, args);
}
template <typename... Types>
inline size_t hash_val(const Types&... args){
size_t seed = 0;
hash_val(seed, args...);
return seed;
}
template <typename T>
inline void hash_val(size_t& seed, const T& val){
hash_conbine(seed, val);
}
inline void hash_combine(size_t& seed, const T& val)
{
seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed<<6) + (seed >> 2);
}
本质上和概述里的例子一样,都是利用可变模板参数的函数递归操作,这里就不多做解释了。
2. 实现函数的递归继承
这里举了一个tuple的实现的例子:
template <typename... Values> class tuple;
template <> class tuple<> {};
template <typename Head, typename... Tail>
class tuple<Head, Tail...>
: private tuple<Tail...> //注意这里的私有继承
{
typedef tuple<Tail...> inherited;
public:
tuple() {}
tuple(Head v, Tail... vtail)
:m_head(v), inherited(vtail...) {}
Head head() { return m_head; }
inherited& tail() { return *this; } //这里涉及派生类到基类的类型转换
protected:
Head m_head;
};
PPT里解释得很清楚:
用于递归继承实现tuple:
typename Head::type head() {return m_head;}报错
第一种实现方法:优化使用decltype实现
第二种实现方法:直接使用Head作为返回值类型
三、Variadic Templates应用举例
1. 一个简单的print()函数
同print应用举例。
2. 使用variadic templates 重写printf()
逻辑和例子1其实一样,唯一不同的是前面有字符串指示格式,如果不匹配会抛出异常,代码如下:
template <typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
while(*s){
if(*s == '%' && *(++s) != '%'){
std::cout << value;
printf(++s, args...);
return;
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
3. 使用initializer_list定义max()
-
如果max()要接受可变数量的参数,可以使用initializer_list或者variadic templates。
-
如果参数类型相同,使用initializer_list即可,无需使用variadic templates实现。
下面看一下标准库中的max()的例子:
//函数max()的定义
template <typename _Tp>
inline _Tp
max(initializer_list<_Tp> __l)
{
return *max_element(__l.begin(), __l.end());
}
//函数max_element()的定义
template <typename _ForwardIterator,
typename _Compare>
_ForwardIterator
__max_element(_ForwardIterator __first,
_ForwardIterator __last,
_Compare __comp)
{
if(__first == __last) return __first;
_ForwardIterator __result = __first;
while(++__first != __last)
if(__comp(__result, __first))
__result = __first;
return __result;
}
template<typename _ForwardIterator>
inline _ForwardIterator
max_element(_ForwardIterator __first,
_ForwardIterator __last)
{
return __max_element(__first, __last,
__iter_less_iter());
}
//函数__iter_less_iter()的定义
inline _Iter_less_iter
__iter_less_iter()
{ return _Iter_less_iter(); }
//类型_Iter_less_iter的定义
struct _Iter_less_iter
{
template<typename _Iterator1,
typename _Iterator2>
bool
operator()(_Iterator1 __it1,
_Iterator1 __it2) const
{ return *__it1 < *__it2; }
};
4. 参数类型相同,递归调用标准库的std::max()实现maximum()
int maximum(int n)
{
return n;
}
template<typename... Args>
int maximum(int n, Args... args)
{
return std::max(n, maximum(args...));
}
5. 为tuple重载<<运算符(使用类模板)
//output operator for tuples
template <typename... Args>
ostream& operator<<(ostream& os, const tuple<Args...>& t){
os << "[";
PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t);
return os << "]";
}
//print element with index IDX of tuple with MAX elements
template <int IDX, int MAX, typename... Args>
struct PRINT_TUPLE{
static void print(ostream& os, const tuple<Args...>& t){
os << get<IDX>(t) << (IDX+1 == MAX ? "" : ",");
PRINT_TUPLE<IDX+1, MAX, Args...>::print(os, t);
}
};
//partial specialization to end the recursion
template <int MAX, typename... Args>
struct PRINT_TUPLE<MAX, MAX, Args...>{
static void print(std::ostream& os, const tuple<Args...>& t){
}
};
这里关于模板的使用有点我不理解的地方:
(1)类模板可以传入对象而不是类型,看起来有点像函数传参。
(2)下面的偏特化版本,class后面也可以写尖括号,没见过这种写法。模板这块可能还得下点功夫。
6. 用于递归继承
tuple中的递归继承:见第二部分的2
7. 受6的启发,用于递归复合
template <typename... Values> class tup;
template<> class tup<> {};
template<typename Head, typename... Tail>
class tup<Head, Tail...>
{
typedef tup<Tail...> composited;
protected:
composited m_tail;
Head m_head;
public:
tup() {}
tup(Head v, Tail... vtail)
: m_tail(vtail...), m_head(v) {}
Head head() { return m_head; }
composited& tail() { return m_tail; }
};