16.1.1 函数模板
<pre name="code" class="cpp">template <typename T>
int compare(const T &v1, const T &v2)
{
if(v1 < v2) return -1;
if(v2 < v1) return 1;
return 0;
}
模板定义以 关键字template开始,后跟一个模板参数列表,这是一个逗号分隔的一个或多个 模板参数的 列表, 用< > 包围起来。
在模板定义中,模板参数列表不能为空。
模板参数列表作用很像函数参数列表。
在使用模板时,我们【显式地或隐式地】指定模板实参,将其绑定到模板参数上。
vector<int> vec1{1, 2, 3};
compare(vec1, vec2);
对于这个调用,编译器会生成另一个compare版本,其中T被替换为vector<int>。这些编译器生成的版本通常被成为模板的实例。
模板类型参数:
可以有默认的,但一般没必要使用,就算使用了,也不会有什么方便之处。
template <typename T = int>
class Foo
{
public:
T t;
};
Foo<> f;//一定要写<>,否则报错
template <typename T = int>
T foo(T* p)
{
T tmp = *p;
//...
return tmp;
};
template <typename T, U> T calc(const T&, const U&);//错误,U前面要加上typename或者class
template <typename T, class U> T calc(const T&, const U&);//正确
template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
return strcmp(p1, p2);
}
调用compare("hi", "mom") 时N和M会被3和4代替(字符串后面加一个空字符)。
更直观的:
template<unsigned N = 1>
int show()
{
cout << N << endl;
}
会打印1.
这里的N是一个常量,在需要使用常量的地方,可以这样使用,比如说指定数组大小。
inline和constexpr的函数模板,inline和constexpr都写在template<>之后,返回类型之前。
模板类型应该尽量减少对实参类型的要求。
模板编译:当编译器遇到一个模板定义时,并不生成代码。只有当我们实例化出模板的一个特定版本时,编译器才会生成代码。定义模板时,不生成代码。
调用函数时,只需要掌握声明。使用类类型的对象时,类定义必须是可用的,但成员函数的定义不必已经出现。因此,我们将类定义和函数声明放在头文件中,普通函数和类的成员函数的定义放在源文件中。模板则不同:为了生成实例化版本,编译器需要掌握模板或类模板成员函数的定义。因此,模板的头文件通常既包含声明也包含定义。
函数模板和类模板成员的定义通常放在头文件中。
类模板包含两种名字:
- 那些不依赖于模板参数的名字。
- 那些依赖于模板参数的名字。
16.1.2 类模板