第一章 Function Templates
- C++ 中equality 和 equivalent 的区别
- Equivalent 要求类满足”LessThanCompare”,即这两个实例可以使用”<”进行比较
- Equality 要求满足EqualityCompareable,即可以使用”==”比较
- 从语意上来说,
class
和typename
它们是Equivalent。由于class存在一定的误导性,所以推荐使用typename来声明模板参数T
- typename 主要用于依赖于另一个模板参数的嵌套类型时进行声明,如下例:
1
2
3
4
5template <typename param_t>
class Foo
{
typedef typename param_t::baz sub_t;
}; - 在显式实例化模板时,必须使用class关键字
1
template class Foo<int>;
- 对于不同类型的实例的实现,都会根据传入的参数来对应实现
- 模板的二阶段检查(Two-Phase Translation)
- 第一阶段检查: 在未生成实例的定义期,检查未使用模板参数的代码正确性
- 语法是否有误
- 是否使用了不发现的变量、函数名
- static assertions 检查
- 第二阶段检查: 在对应生成实例期间,检查依赖于模板参数的代码是否都有效。基本上,所有代码都需要经过第二阶段检查
- 当且仅当有一个template type时,传入的值的类型必须与第一个值的类型保持一致。如果需要类型不一致,可通过以下几种方法来处理
- 使用staic_cast 来解决
- Eg: max(static_cast
(4),7.2); // ok
- Eg: max(static_cast
- 使用显式声明来避免自动类型推导
- Eg: max
(4,7.2); //ok
- Eg: max
- 可以声明多个模板参数
- 在C++中,Return Type 是不能从调用者使用该调用的上下文中推断出来的,可以使用如下方法来处理return type
- 在实例化时,通过显式指定return type
1
2
3
4template <typename T>
T max( T a, T b);
...
::max<double>(4,7.2); - 当定义多个template parameter时,可以通过指定第一个为return type,实例化时仅需显式指定return type就可以了
1
2
3
4template <typename RT,typename T1,typename T2>
RT max(T1 a,T2 b);
...
::max<double>(4,8.3);
在不自定义return type情况下,可以使用如下方法来决定return type:
使用auto进行类型的自动推导
使用自动推导函数 * C++ 11 版本可以使用decltype()函数,其写法如下
auto add(T x,U y) -> decltype(x+y);
* C++ 14版本可以直接使用auto类型推导,原因:C++14版本使得普通函数具备返回值推导的能力可以使用std::common_type函数(std::common_type 推导出一个可以覆盖所有所定义类型的通用类型) * C++ 11版本使用std::common_type() * C++ 14版本使用std::common_type_t()
在实例化重载template Function 过程中,会根据参数优先匹配非模板函数,不匹配才会寻找对应的模板函数相应实现
为何使用值传递而不使用引用传递的原因
值传递语法更简单
编译器对应优化更好
move语意使得拷贝更方便
有时根本没有move和copy
第二章
- 在class template中,使用类名但不加模板参数代表类和模板参数作为它的实际参数,如下例所示
1
2
3
4
5
6
7
8
9
10
11
12
13template <typename T>
class Stack {
...
Stack (Stack const&);
Stack& operator= (Stack const&);
};
//等价于下面的代码
template <typename T>
class Stack<T> {
...
Stack<T> (Stack<T> const&);
Stack<T>& operator= (Stack<T> const&);
}; - C++ template 定义变量时需要注意”>”的使用
- C++ 11版本之前,两个相邻的”>”中间需要安插一个空格
- Eg: Stack<Stack
>
- Eg: Stack<Stack
- C++ 11版本及之后的版本,都可以直接写入两个”>”而不需要空格
- Eg: Stack<Stack
>
- Eg: Stack<Stack
- 如果想要特化一个class template,那么需要提前声明template<>和一个具有规范类型的类
- 类将作为模板参数
- 类型将被直接指定声明在类名中
- 在一些特殊情况下采用偏特化会更好
- 与全特化基本类似,但是偏特化可以定义单个/多个参数
- 模板参数也需要被直接指定在类名的定义中
- 默认class template parameters可以定义为一个容器用来管理数据
- 也可以在main(),对于想要使用的容器进行显式指定
- 通用的类型别名
- 使用关键字 typedef,用法如下
1
2
3typedef Stack<int> IntStack; // typedef 关键字
void foo (IntStack const& s); // s is stack of ints
IntStack istack[10]; // istack is array of 10 stacks of ints - 自C++ 11版本之后,可以使用关键字
using
1
2
3using IntStack = Stack<int>; //alias declaration
void foo (IntStack const& s); //s is stack of ints
IntStack istack[10]; //istack is array of 10 stacks of ints - IntStack 和 Stack
是两个可互换符号的相同类型 - 对于已经存在的类型,如果需要定义一个别名。推荐使用typedef,这样会使得代码可读性更好。
- C++11之后,我们可以alias templates,用法如下
1
2template <typename T>
using DequeStack = Stack<T, std::deque<T>>;
- class template 参数推导会根据字符串字面来进行推导,如下例
1
2
3
4
5Stack (T const& elem) : elems({elem}) {} // initialize stack with one elemet
Stack stringStack = "bottom"; //Stack<char const[7]> C++17版本推导结果
//这种写法会导致在T确定为char const[7],将无法修改,所以应改写成
Stack (T elem) : elems({elem}) {};
Stack stringStack = "bottom" // Stack<char const*> C++ 17自动推导