条款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. linux yum配置代理

    yum里面可以单独设置代理就是yum源的参数加proxy=“http://ip:PORT”即在/etc/yum.conf中加入下面几句.proxy=http://210.45.72.XX:808pro ...

  2. Java开发笔记(四十五)成员属性与成员方法

    前面介绍了许多数据类型,除了基本类型如整型int.双精度型double.布尔型boolean之外,还有高级一些的如包装整型Integer.字符串类型String.本地日期类型LocalDate等等,那 ...

  3. LeetCode 176. 第二高的薪水(MySQL版)

    0.前言 最近刷LeetCode 刷数据库题目 由于数据库课上的是SQL,而MySQL有许多自己的函数的,怕把刚学会的函数忘记 特在此记录! 1.题目 编写一个 SQL 查询,获取 Employee ...

  4. 由AbstractQueuedSynchronizer和ReentrantLock来看模版方法模式

    在学完volatile和CAS之后,近几天在撸AbstractQueuedSynchronizer(AQS)的源代码,很多并发工具都是基于AQS来实现的,这也是并发专家Doug Lea的初衷,通过写一 ...

  5. 浏览器与Node的事件循环(Event Loop)有何区别?

    前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...

  6. 安装完成Dynamics 365 CE后别忘了更改维护作业的运行时间

    摘要: 微软动态CRM专家罗勇 ,回复309或者20190308可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 安装完毕Dy ...

  7. LEDAPS1.3.0版本移植到windows平台----HuPm参数初始化模块

    这个是2012年左右放在百度空间的,谁知百度空间关闭...转移到博客园. 最近项目用到3.1.2版本的LEDAPS,新版本的使用情况会在后续文章中慢慢丰富. LEDAPS的调用顺序是:HuPm--&g ...

  8. angular反向代理配置

    Angular-cli 是基于webpack 的一套针对提升angular开发体验的命令行工具. 开发vue的时候,基于webpack的时候当时配置一个反向代理以完全实现前后端分离的体验,既然webp ...

  9. SQL SELECT DISTINCT 语句

    SQL SELECT DISTINCT 语句 在表中,可能会包含重复值.这并不成问题,不过,有时您也许希望仅仅列出不同(distinct)的值. 关键词 DISTINCT 用于返回唯一不同的值. 语法 ...

  10. js 学习之路5:使用js在网页中添加水印

    示例: <!DOCTYPE html> <html> <meta http-equiv="Content-Type" content="te ...