C++可变参数模板的小知识
2015-10-05 00:50
316 查看
函数模板的类型参数(即模板实参)可以推导,不需要显示的写出来;但是类模板的类型参数必须显示的写出来。
函数模板是不能偏特化的(可以全特化),这是很神奇的地方,貌似在C++11中仍然是这样。不过由于函数模板可以重载,一样可以达到偏特化的效果;实际上,函数模板偏特化和重载的界线非常模糊。
以下是全特化的例子:
自己利用可变参数模板实现的一个有std::function 部分功能的类:
可以包装普通函数跟成员函数,标准库中的function,包装成员函数时,第一个参数可以是普通传值类型,也可以是指针类型,也可以是引用类型。
关于可变参数模板的参数包:打包的过程是将 ... 省略号的左右由逗号分离的一个模式打包一起;解包是按 ... 省略号前边的模式匹配着展开。
需要注意的是,后边的function<> 是前边的一个特化,虽然这里模板的类型参数的个数不相同。所以我的总结是,无论类模板还是函数模板,只要有两个尖括号<>, 一个在template 后,一个在模板id中,都是基本模板的一个特化版本,而不管模板的类型参数的个数。
函数模板的类型参数在实例化是可以推导,但是全特化(函数模板不支持偏特化),仍然需要写全函数模板的模板id,因为此时是定义。
下边是对 tuple 的解释:代码在这里
函数模板是不能偏特化的(可以全特化),这是很神奇的地方,貌似在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 haed; //展开之后 每一层里都有个独立的 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, <span style="color:#FF0000;">tuple<T, Ts...></span>> { //注意这里每次剥离tuple 类型参数中的前边一个 通过k来计数控制 相当于在递归中使用计数来控制递归的层数 typedef typename elem_type_holder<k - 1, <span style="color:#FF0000;">tuple<Ts...></span>>::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...></span>& base = t; //还有这里基类引用指向的派生类 return get<k - 1>(base); //一直这么剥离下去 直到能匹配上边一个重载版本 }
相关文章推荐
- C/C++ 拾遗
- c++组合模式和原型模式一起使用
- 带你玩转Visual Studio——带你发布自己的工程库
- 10.4做题--USACO1.2方格转换
- C/C++中typedef关键字总结
- 【小游戏】俄罗斯方块(C++版)
- 【小游戏】俄罗斯方块(C++版)
- C语言的链接属性的个人理解
- C++primer学习:string类的练习(2)
- C++primer学习:string的操作习题(1):
- CPPUTest 单元测试框架(针对 C 单元测试的使用说明)
- C++学习笔记6——类的多态
- 【小游戏】C++打造2048控制台版本
- 【小游戏】C++打造2048控制台版本
- C++构造函数以及析构函数的若干面试问题
- 《C++primer(第五版)》学习之路-第十七章:标准库特殊设施
- C#学习日记10----番外篇 C语言进制转换
- C++函数
- [C/C++]函数可变参数表
- 条款5.了解c++默默编写并且调用了哪些函数。