条款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;
};

《Effective C++》模板与泛型编程:条款32-条款40的更多相关文章

  1. Effective C++ —— 模板与泛型编程(七)

    C++ templates的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates,他们发现templates有能力完成愈多可能的变化 ...

  2. Effective C++ ——模板和泛型编程

    条款41:了解隐式接口和编译器多态 以public继承的类,

  3. Effective C++阅读笔记 较详细 复杂条款带样例

    一.让自己习惯C++ 条款01:视C++为一个语言联邦 C++可视为: C:以C为基础. 面向对象的C++:添加面向对象特性. 模板C++:泛型编程概念,使用模板. STL:使用STL的容器.迭代器. ...

  4. 【effective c++】模板与泛型编程

    模板元编程:在c++编译器内执行并于编译完成时停止执行 1.了解隐式接口和编译期多态 面向对象编程总是以显式接口(由函数名称.参数类型和返回类型构成)和运行期多态(虚函数)解决问题 模板及泛型编程:对 ...

  5. EC读书笔记系列之15:条款32、33、34

    条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...

  6. 《Effective C++》继承与面对对象设计:条款32-条款40

    条款32:确定你的public继承塑模出is-a关系 public继承意味着is-a.适用于base class身上的每一个函数也一定适用于derived class. 条款33:避免遮掩继承而来的名 ...

  7. C++ 模板与泛型编程

    <C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存 ...

  8. C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]

    模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  9. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  10. C++ Primer 学习笔记_77_模板与泛型编程 --实例化

    模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...

随机推荐

  1. ajaxFileUpload上传带参数,返回值改成json格式

    /*直接复制在自己的js文件中就能使用*/ jQuery.extend({ createUploadIframe: function (id, uri) { //create frame var fr ...

  2. Mybatis框架基础支持层——日志模块(8)

    前言: java开发中常用的日志框架有Log4j,Log4j2,Apache Commons Log,java.util.logging,slf4j等,这些工具对外的接口不尽相同.为了统一这些工具的接 ...

  3. Linux系统启动详解

    系统启动流程 通过下图认识下Linux系统的总体启动流程. BIOS BIOS一般负责检查硬件和查找启动设备. MBR:Boot Code MBR只是一段引导代码,真正的引导是由引导程序去执行的. G ...

  4. Vsphere 回收未消使用的磁盘空间

    下载sdelete.exe 执行 sdelete.exe -z E: ,然后又恢复为原可用空间 关机   SHH进入物理主机,找到对应的虚机文件 执行vmkfstools -K test-Win200 ...

  5. BIM与GIS

    BIM行业是建筑与IT结合而形成的一个新兴行业,既然能说是行业,说明它包含的内容非常丰富,懂一点和完全懂是两码事,就好像一滴水和一片大海的范围一样.现在国内有很多高校开设了BIM专业,并对口招收了学生 ...

  6. Android Studio遇到Failed to sync Gradle project错误时的解决办法

    一   报错显示 Gradle sync failed: Unknown host 'd29vzk4ow07wi7.cloudfront.net'. You may need to adjust th ...

  7. nodejs 使用 js 模块

    nodejs 使用 js 模块 Intro 最近需要用 nodejs 做一个爬虫,Google 有一个 Puppeteer 的项目,可以用它来做爬虫,有关 Puppeteer 的介绍网上也有很多,在这 ...

  8. Java 集合系列(二)—— ArrayList

    ArrayList ArrayList 是通过一个数组来实现的,因此它是在连续的存储位置存放对象的引用,只不过它比 Array 更智能,能够根据集合长度进行自动扩容. 假设让我们来实现一个简单的能够自 ...

  9. Nginx反向代理实现IP访问分流

    通过Nginx做反向代理来实现分流,以减轻服务器的负载和压力是比较常见的一种服务器部署架构.本文将分享一个如何根据来路IP来进行分流的方法. 根据特定IP来实现分流 将IP地址的最后一段最后一位为0或 ...

  10. Windows之MySQL安装教程

    MySQL安装说明 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,My ...