对应《c++ primer 第五版》的第16章前半部分,这里是一个简单的总结。
为了防止对每个不同的类型都定义一个函数,可以使用通用的模板,一个函数模板就是一个公式,可用来生成针对特定类型的函数版本。
模板定义以template开始,后面跟一个模板参数列表,用< >括起来。 模板定义中,模板参数列表不能为空。
模板参数列表就像函数的参数列表一样,在运行时要用实参来初始化形参。 当使用模板时,显式或隐式地指定模板实参,将其绑定到模板参数上。 比如定义了一个模板函数compare
template<typename T> int compare(const T &v1, const T &v2)
使用时,如果这样用
compare(1, 0)
就相当于instance化了一个特定版本的函数,T绑定了int。
声明为inline或者constexpr的,inline这个关键字要放在模板参数列表之后,返回类型之前,如下
template <typename T> inline T min(const T&, const T&)
类型参数前必须使用关键字class 或 typename, 可以看到上面都用的是typename, 也可以用
template <class T>
因为typename是在模板已经广泛使用之后才引入cpp语言中的,所以一些程序员仍然只用class
每个类型参数前必须使用class 或者 typename, 下面这样做是不对的
template <typename T, U> //这样不对,U前也必须用typename 或 class
说下模板的编译:
编译器遇到一个模板定义时,并不生成代码,只有当实例化时才会生成代码。 大多数编译错误是在实例化期间报告的 第 1 阶段编译模板本身时,会检查语法错误,比如忘记分号。 第 2 阶段 编译器遇到模板使用时,对函数模板的调用,会检查实参数目是否正确。 第 3 阶段实例化时,只有这个阶段才能发现类型相关的错误,这类错误可能在link时才报告。
类模板:
类模板也是以关键字template开始,后面跟模板参数列表
template <typename T> class Blob { ... };
这里要说下阅读模板类代码时,要记住类模板的名字不是一个类型名,比如
template<class SEMANTICS> bool SemanticsOcTreeNode<SEMANTICS>::isSemanticsSet() const { return this->semantics.isSemanticsSet(); } SEMANTICS semantics;
可以看到isSemanticsSet函数是semantics对象的函数,但是要知道SEMANTICS并不是一个类型名,它具体是什么类型,要看调用它的地方传的是什么实参类型。
和其他类相同,既可以在类模板内部,也可以在类模板外部定义其成员函数。 且定义在类模板内的成员函数被隐式声明为inline函数。
为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,因此,函数模板和类模板成员函数的定义通常放在头文件中。
在类模板自己的作用域中,可以直接使用模板名而不提供实参,如下
template <typename T> class B { ... B& operator++(); //这里不需要指定B<T> };
但是,当定义在类模板外时,就要加上
friend class
如果模板类包含一个非模板friend class, 那么友元被授权可以访问所有模板实例, 如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例。 具体下次再码。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/290323.html