非类型模板形参: 模板的非类型形参也就是内置类型形参
如: template<class T, int a> class A{}; 其中 int a 就是非类型的模板形式参. 非类型形参在模板定义的内部是常量值, 也就是说非类型形参在模板的内部是常量.
- #include <iostream>
- using namespace std;
- template<class T, int b>
- class A
- {
- public:
- A()
- :a(b){}
- void show( )
- {cout<<a<<endl;}
- protected:
- T a;
- };
- int main(void)
- {
- const int i = 10;
- A<int, i> a;
- a.show();
- return 0;
- }
1. 调用非类型模板形参的实参必须是一个常量表达式, 即它必须能在编译时计算出结果. sizeof 表达式的结果是一个常量表达式, 全局变量的地址或引用, 全局对象的地址或引用 const 类型变量也是常量表达式 , 它们可以用作非类型模板形参的实参.
2. 非类型模板的形参只能是整型, 指针和引用, 像 double,String, String ** 这样的类型是不允许的. 但是 double &,double *, 对象的引用或指针则是可以的.
3. 任何局部对象, 局部变量, 局部对象的地址, 局部变量的地址都不是一个常量表达式, 都不能用作非类型模板形参的实参. 全局指针类型, 全局变量, 全局对象也不是一个常量表达式, 不能用作非类型模板形参的实参.
4, 当模板的形参是整型时调用该模板时的实参必须是整型的, 且在编译期间是常量
比如: template <class T, int a> class A{};
如果有 int b, 这时 A<int, b> m; 将出错, 因为 b 不是常量, 如果 const int b, 这时 A<int, b> m; 就是正确的, 因为这时 b 是常量.
5. 非类型模板形参的形参和实参间所允许的转换:
. 模板形参
1. 类模板的类型形参默认值形式为: template<class T1, class T2=int> class A{...}, 这样来为模板中的第二个形参 T2 提供 int 型的默认值.
2, 模板形参可以为类模板的类型形参提供默认值, 但不能为函数模板的类型形参提供默认值. 函数模板和类模板都可以为模板的非类型形参提供默认值.
3, 类模板类型形参添加默认值的规则和函数默认参数规则一样. 如果有多个类型形参, 参数从右向左连续的缺省, 因为要符合的参数从右向左的入栈规则. 比如 template<class T1=int, class T2>class A{}; 就是错误的, 因为 T1 给出了默认值, 而 T2 没有设定.
4, 在类模板的外部定义类中的成员时 template 后的形参表应省略默认的形参类型.
比如 template<class T1, class T2=int> class A{public: void func();};
定义方法为 template<class T1,class T2> void A<T1,T2>::func(){}, 将 int 省略掉.
大致了解了上面模板的一些用法, 我们可以来实现一个东西 -- 适配器
- // 紧接上面 List 的头文件
- ..................
- template <class T, template<class>class Container>
- class Vector
- {
- public:
- void Push( const T& x)
- {
- _con.PushBack(x);
- }
- void Pop()
- {
- _con.PopBack();
- }
- size_t Size()
- {
- return _con.Size();
- }
- bool Empty( )
- {
- return _con.Empty( );
- }
- void Print( )
- {
- _con.Print( );
- }
- private:
- Container<T> _con;
- };
- void test( )
- {
- Vector<string, List> v;
- v.Push("hello");
- v.Print( );
- cout<<v.Empty();
- }
这里用到了上面实现的双向链表, 以此简陋地来模拟 STL 里面的 vector(其实这里用链表版本并不合适, STL 里面用到是顺序表).
然后来看看模板参数里的这个 template<class> class Container , 第一次看的话, 可能你我多少会有些小慌, 毕竟出来个这么长的怪物. 细细来看 其实就好理解了, template<class > 指明了这是模板参数且是模板类类型, 后面取了名字叫 Container. 所以这就相当于声明 Container 是一个模板类类型的类模板参数. 这算是一种固定搭配, 念着有些拗口, 记住就好.
然后就是下面的 Container<T> _con; 它意思就是创建一个 Container 的对象, 而我们也知道这个对象也是模板类对象, 所以把 < T > 给它传进去. 然后我们现在来说一下这个代码有什么用?
其实这里的 Vector 类相当于一个适配器. 适配器有一种 "让一种事物的行为类似于另外一种事物行为" 的机制, 它对容器进行包装, 使其表现出另外一种行为. 针对不同类型的数据, 在存, 删数据时, 我们不用再去实现不同版本的线性表; 我们直接去用 List 类里面的函数, 具体就是直接定义一个 List 的对象, 然后直接拿走进行使用, 最后让 Vector 管理不同类型数据, 进而完成适配; 再比如 STL 里面一个管理 Int 数据的栈, 它的实现是 stack<int, vector<int>> 的, 其内部其实是使用顺序容器 vector<int > 来存储数据 (相当于是 vector<int > 表现出了栈的行为). 这些都是灵活的复用的体现.
最后, 模板不支持分离编译
还是用上面的交换函数举例, 不过这时我们将. h 和. cpp 文件分开.
- ***************Swap.h************
- #include <iostream>
- using namespace std;
- template<class T>
- void Swap(T& a, T& b);
- ***************Swap.cpp************
- #include "Swap.h"
- template<class T>
- void Swap(T& a, T&b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
- ***************main.cpp************
- #include "Swap.h"
- int main(void)
- {
- int a =1, b=2;
- Swap<int>(a, b);
- cout<<a<<" "<<b<<endl;
- return 0;
- }
来源: https://www.cnblogs.com/tp-16b/p/9031722.html