Effective C++ 第7章 读书笔记

第7章 模板与泛型编程

模板元编程

在C++编译器内执行,并于编译完成时停止执行

条款41:了解隐式接口和编译器多态

编译器多态

哪一个重载函数该被调用

运行期多态

哪一个虚函数该被绑定

条款42:了解typename的双重意义

template<typename C>
void print2nd(const C& container){
	if(container.size() > 2){
		**typename** C::const_iterator iter(container.begin());
		……
	}
}

这里的这个typename是必须滴

任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置放上关键字typename

  • typename不可以出现在base classes list内的嵌套从属类型名称之前,也不可以在member initialization list(成员初始列表)中作为base class修饰符。例如:
template<typename T>
class Derived : public Base<T>::Nested{//这里Base<T>的前面就不能用typename
public:
    explicit Derived(int x): Base<T>::Nested(x){//同样,这里Base<T>的前面也不能用typename
        typename Base<T>::Nested temp;
        //……
    }
};
  • 常见typename的例子:
template<typename IterT>
void workWithIterator(IterT iter){
    typedef typename std::iterator_traits<IterT>::value_type value_type;
    value_type temp(*iter);
    //……
}

如果IterT是vector::iterator,temp的类型就是int,如果IterT是vector::iterator,temp的类型就是string

这是标准traits class的一种运用

条款43:学习处理模板化基类内的名称

三种办法令C++“不进入 templatized base classes 观察”的行为失效

  1. 在base class函数调用动作之前加上this->
  2. 使用using声明式
  3. 明白指出被调用的函数位于base clase内

条款44:将与参数无关的代码抽离template

共性与变性分析

  • 对于函数,抽出两个函数的共同部分,把它们放进第三个函数中。
  • 对于class,把共同部分搬到新class中,然后使用继承或复合,令原先的classes取用这共同特性。
template<typename T, std::size_t n>
class SquareMatrix{
public:
	void invert();
};

SquareMatrix<double, 5> 和SquareMatrix<double, 10>会具现化两份invert,这两份invert只有常量5和10不同,其余的内容完全相同

所以,进行改进:

template<typename T>
class SquareMatrixBase{
protected://注意这里是protected
	void invert();
	T* pData;
};

template<typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T>{
private:
	using SquareMatrixBase<T>::invert;
	T data[n * n];
public:
	void invert(){this->invert(n);}//这是一个inline调用,this的作用防止invert这个名字被派生类掩盖
};

上面这种方法也有缺点,可能会造成对象所需的内存变大:

base class为了处理矩阵数据,需要一个T*指针,因此,一个SquareMatrix 对象会增大一个指针的大小

💡 请记住

  • templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系
  • 因非类型模板参数(上面示例代码中的std::size_t n)而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数
  • 因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述(eg:指针)的具现类型共享实现码

条款45:运用成员函数模板接受所有兼容类型

同一个template的不同具现体之间并不存在什么与生俱来的固有关系(这里意指如果带有base-derived关系的B、D两类型分别具现化某个template,产生出来的两个具现体之间并不带有base-derived关系)

如果需要template产生出的具现体之间带有base-derived关系 ,需要为它写一个构造模板:

template<typename T>
class SmartPtr{
public:
	template<typename U>
	SmartPtr(const SmartPtr<U>& other)//这个也叫**泛化copy构造函数**,这里不需要explicit,因为是隐式转换
		:heldPtr(other.get()){  //存在某个隐式转换可以将一个U*指针转为一个T*指针时才能通过编译	
		//……
	}
private:
	T* heldPtr
}

💡 请记住

  • 请使用成员函数模板生成“可接受所有兼容类型”的函数
  • 如果你声明member templates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copy assignment操作符

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

  • template实参推导过程中不将隐式类型转换函数纳入考虑
template<typename T>
class Rational{
public:
//……
	friend const Rational operator*(const Rational& lhs,const Rational& rhs){//这里的Rational后面的<T>可以加,也可以像这样省略
		return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());
	}
};

这里虽然使用了friend,却与friend的传统用途“访问class的non-public成分”毫不相干。

为了让类型转换可能发生与所有实参身上,我们需要一个non-member函数;

为了令这个函数被自动具现化,我们需要将它声明在class内部;而在class内部声明non-member函数的唯一办法就是令它成为一个friend。

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

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

Traits是一种技术,也是一个C++程序员共同遵守的协议,它们允许你在编译期间取得某些类型信息

tratis引发“编译期发生与类型身上的if……else计算”(条款48)

如何使用一个traits class:

  • 建立一组重载函数(身份像劳工)或函数模版,彼此间的差异只在于各自的tratis参数。令每个函数实现码与其接受值traits信息 相 应和
  • 建立一个控制函数(身份像工头)或函数模版,它调用上述那些“劳工函数”并传递tratis class所提供的信息

💡 请记住

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

条款48:认识template元编程

Template metaprogramminh(TMP,模版元编程)

Template metaprogramminh执行于C++编译期,会导致以下结果:

  • 可以在编译期找出代码错误
  • 较小的可执行文件、较短的运行期、较少的内存需求
  • 编译时间变长

TMP的起手程序:在编译期计算阶乘:

template<unsigned n>
struct Factorial
{
    enum {value = n * Factorial<n-1>::value};
};

template<>
struct Factorial<0>  //一个特化版本结束递归
{                   
    enum {value = 1};//特殊情况
};

int main()
{
    cout << Factorial<5>::value << endl;
    cout << Factorial<20>::value << endl;
    return 0;
}

执行结果:
在这里插入图片描述

TMP能够达成什么目标?

  • 确保量度单位正确
  • 优化矩阵运算
  • 可以生成客户定制之设计模式

💡 请记住

  • TMP可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率
  • TMP可被用来生成“基于政策选择组合”的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个C++程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值