您的位置:首页 > 编程语言 > C语言/C++

细读《Effective C++》之十三

2007-06-05 16:08 309 查看
Chapter 7. Templates and Generic Programming
templates的一个最重要的作用在于让很多原来在运行期的工作提前到编译期间完成。

Item 41:templates和classes一样都是支持interfaces和polymorphics的,而且tempaltes支持implicit interfaces和compile-time polymorphics。

Item 42:typename可以用于标识nested dependent type name,但在base class lists和member initailization list中不可用作base class identifier。

Item 43:模板化基类存在total template specialization情况,有可能导致base classes中的成员函数无法具现,只能通过this->、using declaration或者explicit calling解决。

Item 44:将与参数无关代码抽离templates可在一定程序上解决代码膨胀问题。

Item 45:用member function templates实现的constructors、assignment operations等可接受所有兼容类型,需要注意的是在实现兼容的同时不要忘记inheritance和其它类型间的约束。

Item 46:non-member functions在混合运算的必要我们在Item 24中已经领略,对于模板化的混合运算,不仅仅需要non-member functions,还需要可以implicit conversion的non-member functions,inline friend non-member functions可以。

Item 47:traits classes在编译期类型信息的获取和函数重载使得本来在运行时完成的工作提前至编译期完成,甚至是运行时的错误也提前在编译时被发现。

Item 48:TMP是一种先进的理念和方法,Scott甚至称之为一种language,可见一斑。

Item 45 - 48

条款45:Use member function templates to accept "all compatible types"

普通指针与智能指针的区别在于:

1) 普通指针在资源管理上不及智能指针安全;

2) 普通指针的实现是物理层面的,而智能指针的实现则是逻辑层面的;

3) 普通指针支持implicit conversions,智能指针并不直接支持implicit conversions。

既然智能指针本质上是objects,如果要使用模板类智能指针,并使其支持implicit conversions,就需要为其实现构造模板:

template<typename T>
class SmartPtr {
public:
template<typename U> // member template
SmartPtr(const SmartPtr<U>& other); // for a "generalized
... // copy constructor"
};

为了使之具有继承特性,可以进行如下修改:

template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other) // initialize this held ptr
: heldPtr(other.get()) { ... } // with other's held ptr
T* get() const { return heldPtr; }
...

private: // built-in pointer held
T *heldPtr; // by the SmartPtr
};

template<class T> class shared_ptr {
public:
template<class Y> // construct from
explicit shared_ptr(Y * p); // any compatible

template<class Y> // weak_ptr, or
explicit shared_ptr(weak_ptr<Y> const& r); // auto_ptr

template<class Y>
explicit shared_ptr(auto_ptr<Y>& r);

shared_ptr(shared_ptr const& r); // copy constructor

template<class Y> // generalized
shared_ptr(shared_ptr<Y> const& r); // copy constructor

shared_ptr& operator=(shared_ptr const& r); // copy assignment

template<class Y> // shared_ptr or
shared_ptr& operator=(auto_ptr<Y>& r); // auto_ptr

template<class Y> // generalized
shared_ptr& operator=(shared_ptr<Y> const& r); // copy assignment
...
};

1) Use member function templates to generate functions that accept all compatible types.Things to Remember

2) If you declare member templates for generalized copy construction or generalized assignment, you'll still need to declare the normal copy constructor and copy assignment operator, too.

条款46:Define non-member functions inside templates when type conversions are desired

在Item 24中,将CRational的operator *实现为non-member function,现在来看其模板化形式:

template<typename T>
class Rational {
public:
Rational(const T& numerator = 0, // see Item 20 for why params
const T& denominator = 1); // are now passed by reference
const T numerator() const; // see Item 28 for why return
const T denominator() const; // values are still passed by value,
... // Item 3 for why they're const
};

template<typename T>
const Rational<T> operator*(const Rational<T>& lhs,
const Rational<T>& rhs)
{ ... }

Rational<int> oneHalf(1, 2); // this example is from Item 24,
// except Rational is now a template
Rational<int> result = oneHalf * 2; // error! won't compile

如果将operator*声明为Rational<T>的friend function,这样在编译器在调用operator*时将使用implicit conversions将2转换为Rational<int>:template实参推导将oneHalf、2分开考虑:oneHalf类型为Rational<int>,所以T为int,operator*的第二个参数类型被声明为Rational<T>,但传递的2为int,由于template实参推导不考虑implicit conversions,所以2不会在转换为Rational<int>后再推导T类型。

template<typename T>
class Rational {
public:
...
friend // declare operator*
const Rational operator*(const Rational& lhs, // function (see
const Rational& rhs) // below for details)
{ return doMultiply(lhs, rhs); } // call helper
};

template<typename T> class Rational; // declare Rational template
template<typename T> // declare
const Rational<T> doMultiply(const Rational<T>& lhs, // helper
const Rational<T>& rhs); // template

template<typename T> // define
const Rational<T> doMultiply(const Rational<T>& lhs, // helper
const Rational<T>& rhs) // template in
{ // header file,
return Rational<T>(lhs.numerator() * rhs.numerator(), // if necessary
lhs.denominator() * rhs.denominator());
}

Things to Remember以前使用friend只是为了使一个function或者class可以访问其它类中的non-public部分,而此处的friend则是为了支持所有实参的implicit conversions。

When writing a class template that offers functions related to the template that support implicit type conversions on all parameters, define those functions as friends inside the class template.

条款47:Use traits classes for information about types

不知道traits classes会不会成为新的C++0x标准的一部分,即使不会,我相信traits classes的理念也必将深入人心。

先看一例,STL中有一个tools template名为advance,用以将某iterator移动给定距离:

template<typename IterT, typename DistT> // move iter d units forward;
void advance(IterT& iter, DistT d); // if d < 0, move iter backward

template<typename IterT> // template for information about
struct iterator_traits; // iterator types

template < ... > // template params elided
class deque {
public:
class iterator {
public:
typedef random_access_iterator_tag iterator_category;
...
}:
...
};

template < ... >
class list {
public:
class iterator {
public:
typedef bidirectional_iterator_tag iterator_category;
...
}:
...
};

// the iterator_category for type IterT is whatever IterT says it is;
// see Item 42 for info on the use of "typedef typename"
template<typename IterT>
struct iterator_traits {
typedef typename IterT::iterator_category iterator_category;
...
};

template<typename IterT> // partial template specialization
struct iterator_traits<IterT*> // for built-in pointer types
{
typedef random_access_iterator_tag iterator_category;
...
};

iterator的类型在编译期间即可确定,而if statement的判定将在运行期间进行,为了能够在编译期间确定iterator_category的值,可以使用overloading:我们知道STL的五种iterators除了random access iterator之外,其它iterators均不能随机访问。如果我们能够在编译时获取类型信息,则可根据类型信息进行讨论。traits的要求之一便是对built-in types和user-defined types的表现要一样好:

template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // random access
std::random_access_iterator_tag) // iterators
{
iter += d;
}

template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // bidirectional
std::bidirectional_iterator_tag) // iterators
{
if (d >= 0) { while (d--) ++iter; }
else { while (d++) --iter; }
}

template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // input iterators
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( // call the version
iter, d, // of doAdvance
typename // that is
std::iterator_traits<IterT>::iterator_category() // appropriate for
); // iter's iterator
} // category

Design and implement a traits class:

1) Identify some information about types you'd like to make available (e.g., for iterators, their iterator category).

2) Choose a name to identify that information (e.g., iterator_category).

3) Provide a template and set of specializations (e.g., iterator_traits) that contain the information for the types you want to support.

Use a traits class:

1) Create a set of overloaded "worker" functions or function templates (e.g., doAdvance) that differ in a traits parameter. Implement each function in accord with the traits information passed.

2) Create a "master" function or function template (e.g., advance) that calls the workers, passing information provided by a traits class.

Things to Remember

1) Traits classes make information about types available during compilation. They're implemented using templates and template specializations.

2) In conjunction with overloading, traits classes make it possible to perform compile-time if...else tests on types.

条款48:Be aware of template metaprogramming (TMP)

Scott指出:TMP has two great strengths:

1) It makes some things easy that would otherwise be hard or impossible (Item 47);

2) TMPs execute during C++ compilation.

在本章中,Scott一直在强调TMP是Turing-complete的,他甚至称:TMP's largely a functional language。

// TMP : declare variables
std::list<int>::iterator iter;

// TMP : perform loops
template<unsigned n> // general case: the value of
struct Factorial { // Factorial<n> is n times the value
// of Factorial<n-1>
enum { value = n * Factorial<n-1>::value };
};

template<> // special case: the value of
struct Factorial<0> { // Factorial<0> is 1
enum { value = 1 };
};

// TMP : write and call functions
template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
...
}

advance(iter, 10);

1) Ensuring dimensional unit correctness. 将运行时错误提前至编译时侦测。TMP的目标:

2) Optimizing matrix operations.

3) Generating custom design pattern implementations.

Things to Remember

1) Template metaprogramming can shift work from runtime to compile-time, thus enabling earlier error detection and higher runtime performance.

2) TMP can be used to generate custom code based on combinations of policy choices, and it can also be used to avoid generating code inappropriate for particular types.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: