关键字:“template开头”,“参数列表非空”
定义一定要以:template<…>开头(为了解释为什么 inline 在第 2 位置)
定义中模板参数列表不能为空(模板特例化中参数列表可以为空)。
template<tempname T> inline T funcName(const T &a, const T &b) { return (a + b); } //inline
template<tempname T> T funcName(const T &a, const T &b) { return (a + b); }
template< > void funcName() { } //错误:定义时模板参数不能为空。
关键字:“非类形形参为常数”,“泛型编程规则”,“const引用”
在下面的代码中,其中T为类型形参,类型在编译器进行实例化(instantiate)时确定,T还可以用来做类型转换。v为非类型形参,v的实例必须为常数。
template<class T, T v> void foo(T &a, T &b)
{
b = v;
cout<<"a = "<<a<<ends<<"b = "<<b<<endl;
a = b;
}
int main()
{
int m = 1;
int n = 2;
foo<int, m>(m, n); //error C2971: “foo”: 模板参数“v”:“m”: 局部变量**不能用作非类型参数**
cout<<"a = "<<m<<ends<<"b = "<<n<<endl;
return 0;
}
泛型编程中,函数的参数常常为const引用,来保证参数不仅可为拷贝类型,也可以是非拷贝类型,并且能够保证对源数据的只读性。函数内部的判断符号使用 <,目的是减小函数对要处理类型的要求,因为有的类型肯可能并没有对其他运算符进行重载。
实例化目前我所了解的有两种:
编译器通过传入的实参进行推断类型形参的类型(即模板实参推断:template argument deduction/*推理*/),但是无法为非类形形参进行推断。
在调用函数时进行指定类型形参与非类形形参的类型。
template<class T, T v> void foo(T &a, T &b) { ... }
foo(m, 5); //error C2783: “void foo(T &,T &)”: 未能为“v”推导 模板 参数
/*正确的实例化*/
template<class T, class V> void foo(T &a, V &b) { ... }
foo(5, 5); //推断
template<class T, T v> void foo(T &a, T &b) { ... }
foo<int, 6>(5, 5); //指定
大致与函数模板的定义相同。
template<class T> class Bob
{
public:
void func(T &, T &);
template<class It> void foo();
private:
T data;
};
实例化一般在创建对象时指定类型,也可以进行类的特例化。
Bob<int> b; //实例化
关键字:“隐式内联”,“部分实例化”,“成员模板”
定义在类作用域内的成员函数隐式的被定义为内联函数。
成员函数在外部定义时,开头必须以 template<..> 开头。
template<class T> void Bob<T>::func(T &a, T &b)
{
data = a + b;
}
成员模板函数不能是虚函数。
在类的外部,只有当遇到类名以后,才算该类的作用域。因此
Bob<T>
以后均为 Bob类的作用域,类的作用域中可以将 template
关键字:“定义在头文件”,“部分实例化”
当编译器遇到一个模板定义的时候,它并不生成代码。只有当我们实例化出模板一个特定的版本但是时候(即特例化时),编译器才会生成代码。或者在我们使用模板时,编译器才生成代码。
模板函数和模板类的成员函数通常在头文件中定义。
编译模板的三个过程:编译模板本身,编译器遇到使用模板,模板实例化。
错误一般在第三个阶段,模板实例化中检测到。模板实例化中是部分实例化,并非全部。仅仅实例化用到的模板。
默认模板参数时,跟函数默认实参一样,对于一个模板参数,只有当它右侧的所有参数都有默认实参时,它才可以具备默认实参。
template<class A = int, class B> A foo2() { ... } //error C4519: 仅允许在类模板上使用默认模板参数
默认值在使用模板的时候均可以修改(c++11新标准)
template<class A = int, int b = 0> int foo3() { return b; }
int result = foo3<int, 7>(); //result = 7, ok
int result = foo3<double, 7>(); //result = 7, ok
当面对特定的类型,模板不合适的时候,我们可以为这种类型量身打造一款特例化版本。
//定义
template <bool b, typename B, typename C> class {
...
};
//部分特例化
template <> class<true, int, int> {
...
};
//特例化
template <typename B, typename C> class<false, B, C> {
...
};
学无止境,还有很多未了解的C++模板知识和C++模板的使用(比如traits..),部分列举如下:
控制实例化:在大系统中,多个文件中实例化相同的模板,开销严重。使用显示实例化(explicit/*明确的*/ instantitation)来避免开销。
extern template class Bob; //声明:告诉编译器,在程序的其他位置(或者其他文件中)有string的实例化定义
tempalte int compare (const int&, const int&); //定义
在运行是绑定删除器,在编译时绑定删除器(与智能指针有关)。
模板的参数推断。
理解std:move
转发:有的时候需要将一个或者多个实参连同类型转发给其他函数。
可变参数的模板
sizeof… 运算符
可变参数模板的编写
包扩展
转发参数包
struct和template连用实现traits
struct里面定义了类型和静态常数,需要用结构体名打点调用:
structName::defType i = value;
template<class T, T v>
struct integral_constant {
static const T value = v;
typedef T value_type;
typedef struct integral_constant<T, v> type;
};
int i = struct integral_constant::value;