Effective C++ 读书笔记(七)

7 模板与泛型编程

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

n   classes和templates都支持接口和多态。

n   对classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期。

n   对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具体化和函数重载解析 发生于编译期。

 

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

n   声明template参数时,前缀关键字class和typename可互换。

n   使用typename标识嵌套从属类型名称;但不得在base class lists或member initialization list内以它为base class修饰符。

template<typename  T>

class  Derived: public Base<T>::Nested {

public:

         explicit Derived (int  x) : Base<T>::Nested (x)

         {

                   typename Base<T>::Nested  temp;

                   …

}

};

 

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

class CompanyA

{

public:

         void sendClearText(const std::string&){}

         void sendEncrypted(const std::string&){}

};

 

class CompanyB

{

public:

         void sendClearText(const std::string&){}

         void sendEncrypted(const std::string&){}

};

 

template<typename Company>

class MsgSender

{

public:

         void sendClear(std::string&)

         {

                   Company c;

                   c.sendClearText(std::string(""));

         }

};

class CompanyC

{

public:

         void sendEncrypted(const std::string& msg){}

};

 

template<>

class MsgSender<CompanyC>

{

public:

         void sendSecret(std::string&)

         {

                   CompanyC c;

                   c.sendEncrypted("");

         }

}; //全特化版本,没有实现sendClear

 

template<typename Company>

class LoggingMsgSender: public MsgSender<Company>

{

public:

     void sendClearMsg(std::string&)

     {

         sendClear(std::string("")); //当Company == CompanyZ时,这个函数不存在

     }

};

 

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

Templates生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依联系

因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数。

 

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

template<typename T>

class SmartPtr{

public:

         template<typename U>//member template,生成copy构造函数

         SmartPtr( const  SmartPtr<U>&  other): heldPtr(other.get()) { } //暗示只有U*可转为T*才可通过编译

         T*  get( )const { return heldPtr; }

private:

         T*  heldPtr;

};

原始指针类型之间的转换是隐式转换,因此并未声明为explicit。

 

在class内声明泛化copy构造函数并不会阻止编译器生成它们自己的copy构造函数(non-template),所以如果想要控制copy构造函数的方方面面必须同时声明泛化copy构造函数和正常的copy构造函数。同理,适用于赋值操作。

template<class  T>

class  shared_ptr{

public:

         shared_ptr ( shared_ptr const&  r);

         template<class  Y>

         shared_ptr( shared_ptr<Y>  const&  r);

         shared_ptr&  operator = (shared_ptr const&  r);

         template<class  Y>

         shared_ptr&  operator = (shared_ptr<Y> const&  r);

         …

};

 

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

template<typename T>

class Rational

{

public:

     Rational(const T& numerator = 0, const T& denominator = 1):n(numerator),d(denominator){}

     T numerator()const{return n;}

     T denominaotr()const{return d;}

private:

     T n, d;

};

template<typename T>

const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs)

{

return Rational<T>(lhs.numerator()*rhs.numerator(), lhs.denominaotr()*rhs.denominaotr());

}

 

Rational<int> oneHalf(1,2);

Rational<int> result = oneHalf * 2; //编译error

operator*第一参数被声明为Rational<T>,而传给operator*的第一实参的类型是Rational<int>,所以T是int。第二参数是Rational<T>,但传入的实参是整数2。template实参推导过程中并不考虑通过构造函数而发生的隐式类型转换。因此,不会转换为Rational<int>。

 

解决方法:

template class内的friend声明式可以指涉某个特定函数。因此可以声明operator*是Rational<T>的一个friend函数。编译器总是能够在class Rational<T> 具现化时得知T。

template<typename T>

class Rational

{    …

     friend const Rational<T> operator*(const Rational& lhs, const Rational& rhs);

};

 

template<typename T>

const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs){ … }

可以通过编译,但不能链接。

编译器知道我们要调用哪个函数,但那个函数只被声明于Rational内,并没有被定义出来。尽管我们意图令此class外部的operator* template提供定义式,但是行不通。

最简单的方法是:

template<typename T>

class Rational

{ …

friend const Rational<T> operator*(const Rational& lhs, const Rational& rhs)

{

return Rational<T>(lhs.numerator()*rhs.numerator(), lhs.denominaotr()*rhs.denominaotr());

}

};

 

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

参考<STL源码剖析>内的实现

 

条款48:认识template元编程

TMP是编写template-based C++程序并执行编译期的过程。TMP是以C++写成,执行于C++编译器内的程序。一旦TMP程序结束执行,其输出也就是templates具现出来的若干C++源码,便会一如往常地被编译。

TMP(template metaprogramming)模板元编程可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的行为效率。

TMP是图灵完全的,足以计算任何事物。条款47中的traits就是TMP,traits引发编译期发生于类型身上的if … else计算。

示例:编译期计算阶乘!

template<unsigned n>

struct Factorial{

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

};

template<>

struct Fatorial<0>{

         enum {value = 1};

};

std::cout<<Factorial<5>::value<<endl; //输出120

 

posted on 2012-11-21 22:41  ArcherXu  阅读(141)  评论(0编辑  收藏  举报

导航