《Effective C++》模板与泛型编程:条款32-条款40

时间:2021-11-15 21:43:37

条款41:了解隐式接口和编译期多态

  • class支持显示接口和运行期多态
    • class的显示接口由函数的名签式构成(函数名称、参数类型、返回类型)
    • class的多态通过virtual函数发生在运行期
  • template支持隐式接口和编译期多态
    • template的接口是隐式的,由具体的表达式决定
    • template的多态是通过其具现化和函数重载解析发生在编译期
//这里接口要求T必须实现operator >这个隐式接口
template<typename T>
T max(T a, T b){
return (a > b) ? a : b;
}

条款42:了解typename的双重意义

  • 声明template参数时,前缀关键字class和typename可以互换
  • 使用typename修饰嵌套从属类型名称
    • 不得在base class list(基类列)或member initialization list(成员初始化)内以typename作为base class的修饰符
#include <iostream>
#include <string>
#include <memory> using namespace std; template<typename T>
class Base {
public:
class Nested {
Nested(int _x) {}
private:
T val;
};
}; template<typename T>
class Derived : public Base<T>::Nested { //base class list (基类列)不加typename
explicit Derived(int x) : Base<T>::Nested(x) { //member initialization list(成员初始化)不加typename
typename Base<T>::Nested temp; //嵌套从属类型需要加typename
} }; int main() {
}

条款43:学习处理模板化基类内的名称

Template C++中继承并不是像Object Oriented C++中的那样适用:由于C++考虑到base class templates可能被特化,且特化版本可能不提供和一般template相同的接口。因此它拒绝在derived class中查找templatized base classes(模板化基类)中函数的名字。

  • 解决方法如下
    • 在base class函数调用动作之前加上“this->"
    • 适用using声明式指明模板化基类函数
    • 显示修饰指明模板化基类函数
template <typename T>
class Base{
public:
void print(T a) {cout <<"Base "<< a <<endl;};
}; template<typename T>
class Drive : public Base<T>{
public:
void printf(T a){
print(a); //error 编译器不知道基类有print函数
//解决方案
//this->print();
//using Base<T>::print
//base<T>::print直接调用
}
};

条款44:将与参数无关的代码抽离templates

  • Templates会生成多个classes和多个函数,所以要尽量消除template的膨胀参数
    • 类型模板参数可以让具有完全相同二进制表述的具现类型共享实现代码
    • 非类型模板参数可以用函数参数或class成员变量替换掉
#include <iostream>

using namespace std;

template<typename T>
class Base {
protected:
void show(T a, size_t n) {
cout << "base: " << a << endl;
cout << "size_t: " << n << endl;
}
}; template<typename T, size_t n>
class Derived : private Base<T> {
public:
void show(T a) {
Base<T>::show(a, n);
}
}; int main() {
Derived<int, 5> d;
d.show(1);
}

条款45:运用成员函数模板接受所有兼容类型

  • 使用成员函数模版生成“可接受所有兼容类型”的函数
  • 即使有了“泛化拷贝构造函数”和“泛化的赋值操作符”,仍然需要声明正常的拷贝构造函数和赋值操作符
template<typename T>
class shared_ptr{
public:
//拷贝构造函数,接受所有能够从U*隐式转换到T*的参数
template<typename U>
shared_ptr(shared_ptr<U> const &rh):p(rh.get()){
...
}
//赋值操作符,接受所有能够从U*隐式转换到T*的参数
template<typename U>
shared_ptr& operator= (shared_ptr<U> const &rh):p(rh.get()){
...
} //声明正常的拷贝构造函数
shared_ptr(shared_ptr const &rh);
shared_ptr& operator= (shared_ptr const &rh);
private:
T *p;
}

条款46:需要类型转换时请为模板定义非成员函数

在一个class template内,template名字可以被用来“template和其参数”的简略表达方式

  • 条款24中指出non-member函数能够在所有参数身上实施隐式转换
    • 隐式转换会在调用函数时进行
  • 然而在Template C++中模版函数在运行时会先具现然后再调用函数
    • 所以模版函数的参数在未知类型的情况下不会进行隐式转换
  • 解决方法是将non-member函数写成class template内部的friend函数
    • 使其具现过程在定义类的对象时便完成,再调用函数便可完成隐式转换

条款47:请使用traits class表现类型信息

  • template特化(Template Specialization)
    • 定义当类型模板参数为指定类型,所要运行的程序
//下一函数模板的通用定义:
template<typename T>
struct my_is_void {
static const bool value = false;
}; //对 void 类型,有以下的特化版本:
template<>
struct my_is_void<void> {
static const bool value = true;
}; //测试代码如下:
my_is_void<bool> t1;
cout << t1.value << endl; // 输出0
my_is_void<void> t2;
cout << t2.value << endl; // 输出1
  • template偏特化(Patial Spcialization)
    • 指定类型模板参数中的一部分,或使模板兼容参数的变形
//原始模板
template<typename T>
struct my_is_pointer {
static const bool value = false;
}; //对模板参数T进行限制,要求其为一个指针的类型:
template<typename T>
struct my_is_pointer<T*> {
static const bool value = true;
}; //测试:
my_is_pointer<int> p1;
cout << p1.value << endl; // 输出 0,使用原始模板
my_is_pointer<int*> p2;
cout << p2.value << endl; // 输出 1,使偏特化模板,因为指定了 int * 类型的参数
  • class traits使得“类的相关信息”在编译期就可用
  • 利用重载技术在编译期执行 if...else 语句
//STL五种迭代器
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag : public input_iterator_tag { };
struct bidirectional_iterator_tag : public forward_iterator_tag { }; // STL容器中list,set,multiset,map 和 multimap
struct random_access_iterator_tag : public bidirectional_iterator_tag { }; // STL容器中vector,deque 和 srting //定义容器中的迭代器
template< ... >
class deque {
public:
class iterator {
public random_access_iterator_tag iterator_category;
...
};
...
}; template< ... >
class list {
public:
class iterator {
public bidirectional_iterator_tag iterator_category;
...
};
...
}; //定义iterator_traits响应iterator classs的嵌套式typedef
template<typename IterT>
class iterator_traits {
public:
typedef typename IterT::iterator_category iterator_category;
...
}; //指针特化
template<typename IterT>
class iterator_traits<IterT*> {
public:
typedef typename IterT::iterator_category iterator_category;
...
}; //函数重载
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, random_access_iterator_tag) {
iter += d;
} template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, bidirectional_iterator_tag) {
if (d>=0) { while (d--) ++iter; }
else { while (d++) --iter; }
} template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, input_iterator_tag) {
if (d<0) {
throw out_of_range("Negative distance");
}
while (d--) ++iter;
} //调用
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d, input_iterator_tag) {
doAdvance(iter, d, typename iterator_traits<IterT>::iterator_category());
}

条款48:认识template元编程

  • 本质上就是函数式编程
    • C++元编程可以将计算转移到编译期,执行速度迅速(编译时间边长)
//上楼梯,每次上一步或者两步,有多少种
int climb(int n){
if(n == 1)
return 1;
if(n == 2)
return 2;
return climb(n - 1) + climb(n - 2);
} //元编程,采用类模版
template<int N>
class Climb{
public:
const static int n = Climb<N-1>::n + Climb<N-2>::n;
}; template<>
class Climb<2>{
public:
const static int n = 2;
}; template<>
class Climb<1>{
public:
const static int n = 1;
};