您的位置:首页 > Web前端

<<Effective C++>>读书笔记7: 模板与泛型编程

2016-06-24 09:49 316 查看
每一个Item都很经典,都需要去思考揣摩,我在这里将要点抽象出来,便于日后快速回忆;我只是在做文章的“搬运工”。

        Item 41: 理解隐式接口和编译期多态

1. 显式接口: 它在源代码中显式可见; 显式和隐式接口之间的区别是与 template有关的新内容。

   [隐式接口:模板中的接口,模板类型的不确定导致对象的方法、数据是不确定的;如果没有调用关系,即使没有相应的方法、数据,也可以编译通过]

2. 执行期多态:被调用的特定函数在执行期基于对象的动态类型来确定。

3. 编译期多态:不同的 template 参数实例化函数模板导致不同的函数被调用。

4. 重载函数中哪一个应该被调用的过程,这个发生在编译期;virtual函数调用动态绑定,这个发生在运行期。

        Item 42: 理解 typename 的两个含义

1. typename,它暗示着这个参数不必要是一个class类型(用户自定义类型)。

   [<template typename>相对于<template class>具有更强的覆盖性;除了class类型,还有其它类型的模板参数吗?]

2. 一个 template中的(类、变量)依赖于一个 template参数的名字被称为依赖名字。

   [类型嵌套,内部类型]

3. 当一个依赖名字嵌套在一个 class 的内部时,我称它为嵌套依赖名字。C::const_iterator 是一个嵌套依赖名字,实际上,它是一个嵌套依赖类型名,也就是说,一个涉及到一个类型的嵌套依赖名字。

4. 如果解析器在一个 template 中遇到一个嵌套依赖名字,它假定那个名字不是一个类型,除非你用其它方式告诉它。缺省情况下,嵌套依赖名字不是类型。

   [如果嵌套依赖名字不是类型,编译会出错]

5. 在涉及到一个在 template中的嵌套依赖类型名的时候,必须把单词 typename 放在紧挨着它的前面。

   [用来说明这是一个类型]

6. “typename 必须前置于嵌套依赖类型名” 规则的例外是 typename 不必前置于在一个基类列表中的或者在一个成员初始化列表中作为一个基类标识符的嵌套依赖类型名。

7. "typedef typename" 并列不太和谐,但它是涉及嵌套依赖类型名规则的一个合理的附带结果。

        Item 43: 了解如何访问模板化基类中的名字

1. 当编译器遇到继承“派生类模板”的定义时,它们不知道它从哪个 class继承。当然,它是"基类<T>",但是T是一个 template参数,这个直到更迟一些才能被确定当"派生类"被实例化的时候。不知道T是什么,就没有办法知道“基类<T>” 是什么样子的。特别是,没有办法知道它是否有某一个函数。

   [必须显示引用基类模板中的名字]

2. class定义开始处的 "template <>" 语法。它表示这既不是一个 template,也不是一个独立类。正确的说法是,它是一个用于 template参数为T时的特化版本。

   [模板特化]

3. 基类模板可能被特化,而这个特化不一定提供和通用模板相同的接口,结果,它通常会拒绝在模板化基类中寻找继承来的名字;在某种意义上,当我们从 Object-oriented C++ 跨越到 Template C++时,继承会停止工作。

   [模板!模板!]

4. 编译器是早些诊断对基类成员的非法引用,还是晚些时候被特定的 template参数实例化,C++ 的方针是宁愿早诊断,而这就是为什么当那些 class被从 templates实例化的时候,它不知道类的内容。

5. 在派生类模板中,可以经由 "this->" 前缀,经由 using declarations,或经由一个 explicit基类限定引用基类模板中的名字。

   [看到这里,我有些头疼了,模板是和面向对象不一样的编程思维,如果运用不好会觉得不好懂,不好用,难调试;但模板确实会我的固化编程思维带来一些冲击]

 

        Item 44: 从 templates 中分离出参数无关的代码

1. 使用 templates 可能导致代码膨胀:重复的(或几乎重复的)的代码,数据,或两者都有的二进制码(目标代码)。

2. 在使用templates时,非类型参数(变量)比类型参数(类型)更不通用,但是它们是完全合法的,

   [使用模板传入变量]

3. 在模板化基类中的函数名被派生类隐藏。

   [在模板中,基类模板中的方法被其子类模板所隐藏,必须显示申明调用]

4. 类型参数也能导致膨胀,例如,在很多平台上,int 和 long 有相同的二进制表示。

   [本 Item 只讨论了由非类型模板参数(变量)引起的膨胀]

        Item 45: 用成员函数模板接受“所有兼容类型”

1. 在同一个 template 的不同实例化之间没有继承关系,所以编译器认为CTemplate<Base> 和CTemplate<Derived> 是完全不同的 classes,并不比(比方说)vector<float> 和 Widget 的关系更近。为了得到我们想要的在CTemplate classes 之间的转换,我们必须显式地为它们编程。

2. 从一个类型是同一个 template 的不同实例化的对象创建另一个对象的构造函数(例如,从一个 SmartPtr<U> 创建一个SmartPtr<T>)——有时被称为泛型化拷贝构造函数(generalized copy constructors)。

3. 成员函数模板是一个极好的东西,它们不会改变语言的基本规则。在一个类中声明一个泛型化拷贝构造函数(一个成员模板)不会阻止编译器生成它们自己的拷贝构造函数(非模板的),所以如果要全面支持拷贝构造,必须既声明一个泛型化拷贝构造函数又声明一个“常规”拷贝构造函数。这同样适用于赋值函数。

   [这一小节,我做做了些小调试,详见“成员函数模板”]

        Item 46: 需要类型转换时在 templates 内定义非成员函数

1. 在 template实参推演过程中从不考虑隐式类型转换函数;这样的转换可用于函数调用过程,但是在你可以调用一个函数之前,你必须知道哪个函数存在。为了知道这些,你必须为相关的函数模板推演出参数类型以便你可以实例化出合适的函数。但是在 template 实参推演过程中不考虑经由构造函数调用的隐式类型转换。

2. 在一个模板类中的一个 friend 声明可以指涉到一个特定的函数,我们可以利用这一事实为受到模板实参推演挑战的编译器解围。

3. 在写一个 class template,而这个 class template提供了一些函数,这些函数指涉到支持所有参数的隐式类型转换的 template的时候,把这些函数定义为 class template 内部的 friend。 

   [这一小节很难应用、调试;我可能永远都不知道在一个模板类中声明一个 friend ]

        Item 47: 为类型信息使用 traits 类

1. traits 类使关于类型的信息在编译期间可用。它们使用 templates 和 模板特化(template specializations)实现。 

2. 结合重载,traits 类使得执行编译期类型 if...else 检验成为可能。 

3. 如何使用一个 traits 类:

   *创建一套重载的 "worker"函数或者函数模板,它们在一个 traits 上不同。与传递的 traits 信息一致地实现每一个函数。 

   *创建一个 "master"函数或者函数模板调用这些 workers,传递通过一个 traits 类提供的信息。 

   [本小节中有一点不明白的是:对自定义类型和原始指针的trait使用]

   [对于traits, 我理解的还是不够,后续找时间研读一下STL]   

        Item 48: 感受模板元编程

1. 模板元编程(TMP: template metaprogramming)有两个强大的力量: 首先,它使得用其它方法很难或不可能的一些事情变得容易。第二,因为模板元程序在编译期间执行,它们能将工作从运行时转移到编译时。

   一个结果就是通常在运行时才能被察觉的错误能够在编译期间被发现。另一个结果是使用了 TMP 的 C++ 程序在以下几乎每一个方面都可能更有效率:更小的可执行代码,更短的运行时间,更少的内存需求。

   然而,将工作从运行时转移到编译时的一个结果就是编译过程变得更长。使用 TMP 的程序可能比它们的 non-TMP 对等物占用长得多的编译时间。

   [关于TMP的"hello, world"代表阶乘计算,之前曾做了一点小总结“编译时常量enum ”]  

2. TMP 已经被证明是图灵完备(Turing-complete)的,这意味着它强大得足以计算任何东西。使用 TMP,你可以声明变量,执行循环,编写和调用函数,等等。但是这些结构看起来与其在“常规”C++ 中的样子非常不同。

3. TMP 很大程度上是一个函数性语言,而递归之于函数性语言就像电视之于美国流行文化:是密不可分的。然而,甚至递归都不是常规样式的,因为 TMP loops 不涉及递归函数调用,它们涉及递归模板实例化。

4. 为了领会为什么 TMP 值得了解,更好地理解它能做什么是很重要的。这里是三个示例:

   * Ensuring dimensional unit correctness(确保计量单位正确性) 

   * Optimizing matrix operations(优化矩阵操作) 

   * Generating custom design pattern implementations(生成自定义的设计模式实现)

5. TMP 并不适合于每一个人。它的语法是不符合直觉的,工具支持也很弱;作为一个相对晚近才发现的“附属”语言,TMP的规则仍然带有试验性质。然而,通过将工作从运行时转移到编译时所提供的效率提升还是能给人留下深刻的印象,而表达在运行时很难或不可能实现的行为的能力也相当有吸引力。
   [目前,我对TMP的理解也仅限于“hello, world”; 但是TMP确实带给我了新的编程思想,拓展了我的视野]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: