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

Effective C++ 读书笔记13(43~47)

2012-06-05 23:52 288 查看
条款43:学习处理模板化基类内的名称

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

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

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

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

STL主要由“用以表现容器、迭代器和算法”的template构成,但也覆盖若干工具性的template,例如advance,用来将某个迭代器移动某个给定距离:

template<typename IterT, typename DistT>

void advance(IterT& iter, DistT d);

先了解一下STL迭代器的分类:

input迭代器,只能向前移动,一次一步,只能读一次

output迭代器,只能向前,一次一步,只能写一次

forward迭代器,可以读或写其所指物一次以上

bidirectional迭代器,除了可以向前移动,还可以向后移动

random access迭代器,可以再常量时间内向前或向后跳跃任意距离。

对于5种分类,C++标准程序库分别提供专属的卷标结构加以确认:

struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag: public input_iterator_tag{};
struct bidirectional_iterator_tag: public forward_iterator_tag{};
struct random_access_iterator_tag: public bidirectional_iterator_tag{};


回到advance函数,我们已经知道STL接待器由着不同的能力,我们知道random access迭代器支持迭代器算术运算,只消耗常量时间,因此如果面对这种迭代器,我们希望运用其优势,所以我们希望以这种方式实现advance:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d){

if(iter is a random acdess iterator){
iter += d;
}else{
if(d >= 0){
while(d--) ++iter;
}else{
while(d++);
--iter;
}
}
}


上述方法中首先需要判断iter是否是random access迭代器。trait可以做到,它允许你在编译期取得某些类型信息。trait不是C++关键字,它是一种技术,也是一个C++程序员共同遵守的协议。要求之一是,它对内置类型和自定义类型的表现必须一样好:如果上述advance收到的实参是一个指针和一个int,advance仍然必须有效运作,那意味traits技术必须也能够施行于内置类型如指针身上。

trits必须能够施行于内置类型意味类型内的嵌套信息这种东西出局了,因为我们无法将信息嵌套于原始指针内,因此类型的traits信息必须位于类型自身之外。标准技术是把它放进一个template及其一或多个特化版本中。这样的template在标准程序库中有若然个,其中指针迭代器者被命名为iterator_traits:

template<typename IterT>
struct iterator_traits;//template,用来处理迭代器相关信息


习惯上,traits总是被实现为struct,但往往又被称为traits class。

iterator_traits的运作方式是,针对每一个类型IterT,在struct iterator_traits<IterT>内一定声明某个typedef名为iterator_catagory,这个typedef用来确认IterI的迭代分类。

iterator_traits以两个部分实现上述所言:

1.首先要求每一个用户自定义的迭代器类型必须嵌套一个typedef,名为iterator_catagory,用来确认适当的卷标结构。例如deque的迭代器可随机访问,所以一个针对deque迭代器而设计的class看起来会是这个样子:

template<...>
class deque{

public:
class iterator{
public:
typedef random_access_iterator_tag iterator_category;
//...
};
};


2.iterator_traits只是鹦鹉学舌般的响应iterator class的嵌套式typedef:

//类型IterT的iterator_catagory其实就是用来表现IterT说它自己是什么
//关于typedef typename的运用,请看条款42
template<typename IterT>
struct iterator_traits{
typedef typename IterT::iterator_category iterator_catagory;
//...
}

这对用户自定义类型行得通,但对指针行不通,因为指针不可能嵌套typedef。iterator_traits的第二部分如下,专门用来对付指针。为了支持指针迭代器,iterator_traits特别针对指针类型提供一个偏特化斑斑,犹豫指针的行径于random access 迭代器类似,所以iterator_traits为指针之子那个的迭代器类型是:

template<typename IterT>

struct iterator_traits<IterT*>

{

typedef random_access_iterator_tag iterator_category;

        //...

}

现在,来看如何设计并实现一个traits class:

1.确认若干你希望将来可取的的类型相关信息。例如对迭代器而言,我们希望将来可取的其分类;

2.为该信息选择一个名称,例如iterator_category

3.提供一个template和一组特化版本,内含你希望支持的类型相关信息;

好,现在我们可以对advance实践先前的伪码:

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))
//...
}但是,还有一个问题:IterT类型在编译期间获知,所以iterator_traits<IterT>::iterator_category也可以再编译器确定,但if语句却是在运行期才会核定,这不经浪费时间,也造成可执行文件膨胀。所以可以使用重载。我们需要做的事产生两版重载函数,内含advance的本质内容,但各自接受不同类型的iterator_category对象:
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag)
{
iter+=d;
}

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;
}
}

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;
}

现在来看advance的真实代码:
template<typename IterT, DsitT d>
{
doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category));
}

总结何如使用一个traits class:
1.建立一组重载函数或函数模板,例如doAdvance,彼此间的差异只在于各自的traits参数,令每个函数实现码与其接受之traits信息相应和。

2.建立一个控制函数或函数模板,例如Advance,它调用上述那些函数并传递traits class所提供的信息

trait广泛用于标准程序库,除了供应iterator_category还供应另4份迭代器相关信息,其中最有用的是value_type,见条款42.此外还有char_traits用来保存字符类型的相关信息。

TR1导入许多新的traits classses用以提供类型信息,包括if_fundamental<T>,判断是否为内置类型;is_array<T> 判断是否为数组类型,以及is_base_of<T1, T2>。总计一共添加了50个以上的traits classer

请记住:

1.traits class使得类型相关信息在编译期可用,它们以template和template特化完成时限;

2.整合重载技术后,trait class有可能在编译期对类型执行if..else..测试

条款48:认识template元编程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息