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

对条款24的例子进行模板化:

#include<iostream>
using namespace std;template<typename T>
class Rational{
public:Rational(const T& n = 0, const T& d = 1) :numerator(n), denominator(d){}//构造函数刻意不为explicit,为了隐式类型转换const T getNumerator() const{ return numerator; }const T getDenominator() const{ return denominator; }
private:T numerator;T denominator;
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){return Rational<T>(lhs.getNumerator()*rhs.getNumerator(), lhs.getDenominator()*rhs.getDenominator());
}
int main(){Rational<int> oneEighth(1, 8);Rational<int> oneHalf(1, 2);Rational<int> result = oneHalf*oneEighth;//result = oneHalf * 2;//无法通过编译system("pause");return 0;
}

上述例子中result = oneHalf * 2; 无法通过编译的原因在于实参推导,对oneHalf进行推导,operator*的第一个参数被声明为Rational<T>,而传递的第一个参数是Rational<int>,所以T一定是int;operator*的第一个参数被声明为Rational<T>,而传递的第一个参数是2,因为在template实参推导过程中并不会考虑采纳“通过构造函数而发生的”隐式类型转换,所以2无法转换成Rational<int>进而推导出T为int。

利用如下事实就可以解决上述问题:template class内的friend声明式可以指涉某个特定函数。class template并不依赖template实参推导(后者只施行于function template身上),所以编译器总是能在classRational<T>具现化时得知T。

#include<iostream>
using namespace std;template<typename T> class Rational;
template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs);template<typename T>
class Rational{
public:Rational(const T& n = 0, const T& d = 1) :numerator(n), denominator(d){}const T getNumerator() const{ return numerator; }const T getDenominator() const{ return denominator; }friend const Rational operator*(const Rational& lhs, const Rational& rhs){ return doMultiply(lhs, rhs); }
private:T numerator;T denominator;
};template<typename T>
const Rational<T> doMultiply(const Rational<T>& lhs, const Rational<T>& rhs){return Rational<T>(lhs.getNumerator()*rhs.getNumerator(), lhs.getDenominator()*rhs.getDenominator());
}
int main(){Rational<int> oneEighth(1, 8);Rational<int> oneHalf(1, 2);Rational<int> result = oneHalf*oneEighth;result = oneHalf * 2;cout << result.getNumerator() << "/" << result.getDenominator() << endl;system("pause");return 0;
}

上述例子中的技术虽然使用了friend,却与传统的friend用途“访问class的non-public成分”毫不相干。为了让类型转换可能发生与所有实参身上,我们需要一个non-member函数(条款24);为了让这个函数被自动具体化,我们需要将它声明在class内部;而在class内部声明non-member函数的唯一办法就是让它成为一个friend。

请记住:

  • 当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template内部的friend函数”。

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

1、STL共有5种迭代器:

a、input迭代器,只能向前移动,一次一步。客户只能读取它所指的对象,且只能读取一次。它模仿指向输入文件的阅读指针(read pointer);C++程序中的istream_iterators就是这类的代表。

b、output迭代器,只能向前移动,一次一步。客户只能写入它所指的对象,且只能写入一次。它模仿指向输出文件的涂写指针(write pointer);ostream_iterators是这一类代表。

c、forward迭代器,继承自input迭代器,可以做上述两种迭代器能做的每一件事,而且可以读写它所指的对象一次以上。

d、bidirectional迭代器,继承自forward迭代器,可以向前向后移动。STL中的list、set、multiset、map、和multimap迭代器就是这一类迭代器。

e、randomaccess迭代器,继承自bidirectional迭代器。它可以在常量时间内向前或向后跳跃任意距离,类似原始指针,内置指针就可以当做random access迭代器使用。vector、deque和string的迭代器就是这类。

2、例子:

//deque迭代器是random access迭代器,所以应该这样:
template<...>
class deque{
public:class iterator{public:typedef random_access_iterator_tag iterator_category;...};...
};
//list迭代器可双向行进,所以应该这样:
template<...>
class list{
public:class iterator{public:typedef bidirectional_iterator_tag iterator_category;...};...
};
//对于iterator_traits,只要响应iterator class的嵌套式typedef即可:
template <typename IterT>
struct iterator_traits{typedef typename IterT::iterator_category iterator_category;...
};
//偏特化,以iterator_traits为指针指定的迭代器类型如下:
template<typename IterT>
struct iterator_traits<IterT*>{typedef random_access_iterator_tag iterator_category;...
};
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT& d){if (typeid(typename std::iterator_traits<IterT>::iterator_category)== typeid(std::random_access_iterator_tag))...
}

上述例子说明设计并实现traits class的步骤是:

a、确认若干希望将来可取得的类型相关信息。例如对迭代器而言,希望将来可取得其分类。

b、为该信息选择一个名称(例如iterator_category)。

c、提供一个模板和一组特化版本(如iterator_traits),内含希望支持的类型相关信息。

3、上述例子IterT类型在编译期间获取,所以iterator_traits<IterT>::iterator_category也可在编译期间确定。但if语句却在运行期核定。怎么办?可以使用重载的办法。

例子:

//这份实现用于random access迭代器
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag){iter += d;
}
//这份实现用于bidirectional迭代器
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag){if (d >= 0){while (d--){++iter;}}else {while (d++){--iter;}}
}
//这份实现用于input迭代器
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::input_iterator_tag){if (d < 0){throw std::out_of_range("Negative distance");}while (d--){++iter;}
}
//重载机制调用适当的实现代码
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT& d){doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category());
}

上述例子说明使用一个traits class的方法是:

a、建立一组重载函数或函数模板,彼此间的差异只在于各自的traits参数,令每个函数实现代码与其接受的traits信息相应和。

b、建立一个控制函数或函数模板,它调用上述重载函数并传递traits class所提供的信息。

请记住:

  • Traits classes 使得“类型相关信息”在编译期可用。它们以templates和 template特化完成实现。
  • 整合重载技术后,traitsclasses 有可能在编译器对类型执行if...else 测试。

Effective C++ 条款 48:认识template元编程

1、template metaprogramming(模板元编程)是编写template-based c++程序并执行于编译期的过程。是以c++写成,执行于c++编译器内的程序。

2、模板元编程执行与C++编译期,因此可将工作从运行期转移到编译期,导致的一个结果是某些错误原本在运行期才能侦测到,现在可在编译期找出来,另一个结果是使用TMP的C++程序可能在每一方面更高效:较小的可执行文件、较短的运行期、较少的内存需求。但是将工作从运行期转移到编译期的结果是编译时间能变长了。

3、模板元编程是个图灵完全机器,可以声明变量、执行循环、编写及调用函数……模板元编程并没有真正的循环构件,循环由递归(recursion)完成。TMP递归甚至不是正常的递归,因为TMP递归不涉及递归函数调用,而是涉及“递归模板具现化”(recursive template instantiation)。

例子:

#include<iostream>
using namespace std;template<unsigned int n>
class Factorial{
public:enum{ value = n*Factorial<n - 1>::value };
};
template<>
class Factorial<0>{
public:enum { value = 1 };
};int main(){cout << Factorial<5>::value << endl;cout << Factorial<10>::value << endl;system("pause");return 0;
}

请记住:

  • Template metaprogramming(TMP,模板元编程)可将工作由运行期移到编译期,因而得以实现早期错误侦测和更高的执行效率。
  • TMP可被用来生成“基于政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码。

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/ruan875417/p/4785433.html

【effective c++读书笔记】【第7章】模板和泛型编程(3)相关推荐

  1. Effective Java 读书笔记----第三章

    第三章 对于所有通用的方法 主要讲的是对Object类的非final方法(equals,hashCode,toString,clone和finalize)覆盖的一些规则 1.覆盖equals时请遵守通 ...

  2. Effective C++读书笔记 第1章

    文章目录 Ⅰ. 让自习惯C++ 条款01:视C++为一个语言连邦 条款02:尽量以 const, enum, inline 替换 #define 2.1.采用const替换#define 2.2.采用 ...

  3. Effective C++ 学习笔记 第七章:模板与泛型编程

    第一章见 Effective C++ 学习笔记 第一章:让自己习惯 C++ 第二章见 Effective C++ 学习笔记 第二章:构造.析构.赋值运算 第三章见 Effective C++ 学习笔记 ...

  4. PMP读书笔记(第9章)

    大家好,我是烤鸭:     今天做一个PMP的读书笔记. 第九章 项目资源管理 项目资源管理 项目资源管理的核心概念 项目资源管理的趋势和新兴实践 裁剪考虑因素 在敏捷或适应型环境中需要考虑的因素 9 ...

  5. PMP读书笔记(第2章)

    大家好,我是烤鸭:     今天做一个PMP的读书笔记. 第二章 项目运行环境 2.1 概述 2.2 事业环境因素 2.2.1 组织内部的事业环境因素 2.2.2 组织外部的事业环境因素 2.3 组织 ...

  6. Effective C++读书笔记 摘自 pandawuwyj的专栏

    Effective C++读书笔记(0)       Start   声明式(Declaration):告诉编译器某个东西的名称和类型,但略去细节.   std::size_t numDigits(i ...

  7. Android群英传神兵利器读书笔记——第三章:Android Studio奇技淫巧

    Android群英传神兵利器读书笔记--第三章:Android Studio奇技淫巧 这篇文章篇幅较长,可以使用版权声明下面的目录,找到感兴趣的进行阅读 目录 3.1 Android Studio使用 ...

  8. Effective STL 读书笔记

    Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...

  9. 《深度探索C++对象模型》读书笔记第五章:构造析构拷贝语意学

    <深度探索C++对象模型>读书笔记第五章:构造析构拷贝语意学 对于abstract base class(抽象基类),class中的data member应该被初始化,并且只在constr ...

  10. more effective c++和effective c++读书笔记

    转载自http://bellgrade.blog.163.com/blog/static/83155959200863113228254/,方便日后自己查阅, More Effective C++读书 ...

最新文章

  1. Python使用tpot获取最优模型并抽取最优模型模型参数
  2. Selenium3自动化测试——1. 新建第一个Selenium自动化测试脚本
  3. j - 数据结构实验:哈希表_一看就懂的数据结构基础「哈希表」
  4. 【建议收藏】数学建模竞赛网站汇总
  5. java socket字符串_Java Socket Bug:从Socket的InputStream读取字符串
  6. a标签去掉下划线_html中a标签的一些用法
  7. 为什么你工作努力却没有起色?
  8. Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据
  9. SpringBoot : Spring容器生命周期管理:SmartLifecycle
  10. 【BZOJ1563】【NOI2009】—诗人小G(决策二分栈优化dp)
  11. 基于JAVA+SpringMVC+Mybatis+MYSQL的家庭理财管理系统
  12. STM8单片机低功耗---停机(Halt)模式实现
  13. GID绘图和CDC类
  14. 富文本编辑器(UEditor)的使用
  15. 15数字华容道解法 图解_数字华容道攻略(数字华容道最快解法图)
  16. MC34063+MOSFET扩流 12V-5V 折腾出了高效率电路(转)
  17. 惠普HP Color LaserJet Enterprise MFP M577c 驱动
  18. LiTCTF by lingfeng - (crypto全解)
  19. 前端接入facebook jsSDK,实现登录授权功能
  20. [跬步]说说如何自主学习

热门文章

  1. PolarSSL 1.2.0 发布,SSL 加密库
  2. 透视WPF 应用程序的利器
  3. 学习XHTML的强烈欲望、!
  4. windows中PyCharm的安装和使用
  5. Ethereal使用入门
  6. 时间被空间和运动度量
  7. 形态学图像处理或能帮助实例分割
  8. 我所理解的原型原型链 1
  9. Hugo快速搭建Blog
  10. CentOS 7安装fail2ban+Firewalld防止SSH爆破