Intro

在cpp中的模板,其实就类似于JAVA以及C#中的 <T> 类型. 对于 <T> 有比较深的认知的话,就会明白这东西是有多么的好用了。

在cpp中,由于是可以overload的,所以同名函数会被认为是不同的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int compare(const string& a, const string& b){
  if(a < b){
    return -1;
  }
  if(b < a){
    return 1;
  }
  return 0;
}
int compare(const double& a, const double& b){
  if(a < b){
    return -1;
  }
  if(b < a){
    return 1;
  }
  return 0;
}

那事实上这个东西是可以进行共通化的。

1
2
3
4
5
6
7
8
9
10
template <typename T>
int compare(const T& a, const T& b){
  if(a < b){
    return -1;
  }
  if(b < a){
    return 1;
  }
  return 0;
} 

这个就是最基本的template了。

Definition

以关键字template开始,后面跟一个模板参数列表(template parameter list),这里面可以有一个逗号分隔的一个or多个的模板参数(template parameter)。 在实际应用中,编译器会自动判断传值的类型来进行传值。

1
2
3
compare(1, 0); // int, int
vector<int> v{1, 2, 3};
compare(v, v); // vector<int>, vector<int>

Usage

1
2
3
4
5
6
7
8
9
10
11
12
template <typename T> T foo(T* p){
  T tmp = *p;
  //...
  return tmp;
}

// 以下含义完全相同
// 推荐使用typename
template<typename T, typename U> T calc(const T&, const U&);
template<class T, class U> T calc(const T&, const U&);
template<typename T, class U> T calc(const T&, const U&);
template<class T, typename U> T calc(const T&, const U&);

Nontype Parameter

非类型参数(nontype parameter)。指的是一个值而不是一个类型。

1
2
3
template<unsigned int N, unsigned int M>
int compare(const char (&p1)[N], const char (&p2)[M]);
// 注意,这里是数组的by ref,不是pointer。

ATTENTION

在传递文字列的时候,编译器会自动在字符串末尾插入一个’\0’作为结尾。 所以会这样传输。

1
2
compare("hello", "pa");
int compare(const char (&p1)[6], const char (&p2)[3]);

非类型模板参数(template parameter)可以是整型,pointer或者引用。 非类型参数的实参(tempate argument)必须是常量表达式。

inline/constexpr

tempalte同样可以用于inline/constexpr函数。

1
template<typename T> inline T min(const T&, const T&);

ps: constexpr是常量表达式的函数的定义关键字

Small Cons

上面的代码中都是const类型。

除了少数不可以拷贝的类型(unique_ptr和IO类型等),大多数类型都是可以拷贝的。 不过通过添加const,让我们可以让不可以拷贝的类型也可以被传进来。

上述的代码只使用一种比较符号 <

之所以这样设计的原因是,这样这些类型只需要支持 < 即可,不需要支持 >

这样的细节会影响处理速度,而且我们在使用template的时候应当尽快尽可能的减少对于类型的要求。

Complier

模板编译的时候,并不会生成代码。

只有我们实例出某一个版本的模板的时候,模板才会生成相应的代码。 这一特性影响我们如何debug。

在编译期间,我们调用某一函数的时候,某一函数必须被定义。同样某一个类也是一样。然后,某一个类的成员函数并不需要被提前定义。

所以类定义和函数声明往往在头文件中, 而普通函数和类的成员函数是在源文件中的。

但是模板不一样,为了生成实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,所以模板的头文件通常既包括声明也包括定义。

Next

接下来的章节次序的会跟着书本继续介绍。

Ref

C++ Primer 第五版 16 template


creativ common license