C++可变参数模板的小知识

函数模板的类型参数(即模板实参)可以推导,不需要显示的写出来;但是类模板的类型参数必须显示的写出来。

函数模板是不能偏特化的(可以全特化),这是很神奇的地方,貌似在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);  //一直这么剥离下去  直到能匹配上边一个重载版本
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值