二、语言特性之<Variadic Templates(可变参数模板)>

目录

一、Variadic Templates(可变参数模板)概述

1、谈的是模板Templates:

2、变化的是模板参数:

3、print应用举例

二、Variadic Templates的两个应用

1. 实现函数的递归调用

2. 实现函数的递归继承

三、Variadic Templates应用举例

1. 一个简单的print()函数

2. 使用variadic templates 重写printf()

3. 使用initializer_list定义max()

4. 参数类型相同,递归调用标准库的std::max()实现maximum()

5. 为tuple重载<<运算符(使用类模板)

6. 用于递归继承

7. 受6的启发,用于递归复合






一、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; }
};

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值