《Effective C++》模板与泛型编程:条款32-条款40
条款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的更多相关文章
- Effective C++ —— 模板与泛型编程(七)
C++ templates的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates,他们发现templates有能力完成愈多可能的变化 ...
- Effective C++ ——模板和泛型编程
条款41:了解隐式接口和编译器多态 以public继承的类,
- Effective C++阅读笔记 较详细 复杂条款带样例
一.让自己习惯C++ 条款01:视C++为一个语言联邦 C++可视为: C:以C为基础. 面向对象的C++:添加面向对象特性. 模板C++:泛型编程概念,使用模板. STL:使用STL的容器.迭代器. ...
- 【effective c++】模板与泛型编程
模板元编程:在c++编译器内执行并于编译完成时停止执行 1.了解隐式接口和编译期多态 面向对象编程总是以显式接口(由函数名称.参数类型和返回类型构成)和运行期多态(虚函数)解决问题 模板及泛型编程:对 ...
- EC读书笔记系列之15:条款32、33、34
条款32 确保你的public继承塑模出is-a关系 记住: ★public继承意味着is-a.适用于base class身上的每一件事情一定也适用于derived class身上,∵每一个deriv ...
- 《Effective C++》继承与面对对象设计:条款32-条款40
条款32:确定你的public继承塑模出is-a关系 public继承意味着is-a.适用于base class身上的每一个函数也一定适用于derived class. 条款33:避免遮掩继承而来的名 ...
- C++ 模板与泛型编程
<C++ Primer 4th>读书笔记 所谓泛型编程就是以独立于任何特定类型的方式编写代码.泛型编程与面向对象编程一样,都依赖于某种形式的多态性. 面向对象编程中的多态性在运行时应用于存 ...
- C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]
模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...
- C++ Primer 学习笔记_84_模板与泛型编程 --模板特化
模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...
- C++ Primer 学习笔记_77_模板与泛型编程 --实例化
模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...
随机推荐
- .NET Core整理之配置EFCore
1.新建ASP.NET Core Web应用程序 2.从NuGet下载安装以下工具包 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCo ...
- [Go] golang连接redis测试
go-redis的使用1.下载代码到GOPATH环境变量指定的目录比如我的是进入目录D:\golang\code\src\github.com\go-redis , 执行git clone https ...
- [android]android项目的目录结构
/**************2016年4月23更新*********************/ 相关技术: 知乎:用eclipse做Android开发,新建工程时应如何选择Android的版本? 肥 ...
- MongoDB的存储结构及对空间使用率的影响
MongoDB的存储结构及对空间使用率的影响 使用MongoDB一段时间的同学肯定会发现,MongoDB往往会占用比实际数据大小多不少空间的问题.如果利用db.stats()命令去查看,会发现Mong ...
- grid++报表使用时注意事项
#开始使用:Grid++Report 可以在 Visual C#.Net 与 Visual Basic.Net 下的 WinForm 项目中使用.在项目中使用 Grid++Report 之前,首先必须 ...
- java线程介绍
文章讲解要点 1.线程创建几种方式2.线程常见设置方法,包括优先级.优先级休眠.停止等3.多线程间的数据交互与锁机制4.项目源码下载 线程介绍.png 一.线程创建方式 常见的线程创建方法以下三种 ...
- .net core 验证 Options 参数
.net core 中通过 PostConfigure 验证 Options 参数 Intro 在 .net core 中配置项推荐用 Options 来实现,有一些参数可能必须是用由用户来配置,不能 ...
- DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA
之前在 CSRF 攻击 的那篇文章的最后,我觉得可以用验证码提高攻击的难度. 若有验证码的话,就比较难被攻击者利用 XSS 漏洞进行的 CSRF 攻击了,因为要识别验证码起码要调用api,跨域会被浏览 ...
- linux 按行分割文件
$ sudo awk 'NR%2==1{close(p".txt");++p}{print > p".txt"}' test.txt $ sudo spl ...
- ASP.NET Zero--后端应用程序
后端应用程序 这是用户名和密码输入的实际应用程序.您将主要在此应用程序上添加您的业务需求. 应用文件夹 后端应用程序默认内置在专用区域,名为“ App ”,但可以在创建解决方案时确定.因此,所有控制器 ...