boost源码剖析之:泛型编程精灵type_traits(rev#2)
刘未鹏
C++的罗浮宫(http://blog.csdn.net/pongba)
动机
使用traits的动机一般有三种,分派、效率、使某些代码通过编译。
分派
下面有一个模板函数,假设一个动物收容组织提供了它,他们接受所有无家可归的可怜的小动物,于是他们向外界提供了一个函数接受注册。函数看起来像这样:
template<class T> // T表示接受的是何种动物
void AcceptAnimals(T animal)
{
... //do something
};
但是,如果他们想将猫和狗分开处理(毕竟饲养一只猫和饲养一只狗并不相同。他们可能会为狗买一根链子,而温顺的猫则可能不需要)。一个可行的方法是分别提供两个函数:AcceptDog和AcceptCat,然而这种解决办法并不优雅(想想看,注册者可能既有一只猫又有一只狗,这样他不得不调用不同的函数来注册,而且,如果种类还在增多呢,那样会导致向外提供的接口的增多,注册者因此而不得不记住那些烦琐的名字,而这显然没有只需记住AccpetAnimal这一个名字简单)。如果想保持这个模板函数,并将它作为向外界提供的唯一接口,则我们需要某种方式来获取类型T的特征(trait),并按照不同的特征来采用不同的策略。这里我们有第二个解决办法:
约定所有的动物类(如class Cat,class Dog)都必须在内部typedef一个表明自己身份的类型,作为标识的类型如下:
struct cat_tag{}; //这只是个空类,目的是激发函数重载,后面会解释
struct dog_tag{}; //同上
于是,所有狗类都必须像这样:
class Dog
{
public:
// 类型(身份)标志,表示这是狗类,如果是猫类则为typedef cat_tag type;
typedef dog_tag type;
...
}
然后,动物收容组织可以在内部提供对猫狗分开处理的函数,像这样:
// 第二个参数为无名参数,只是为了激发函数重载
template<class T>
void Accept(T dog,dog_tag)
{...}
template<class T>
void Accpet(T cat,cat_tag) // 同上
{...}
于是先前的Accept函数可以改写如下:
template<class T>
void Accept(T animal) //这是向外界提供的唯一接口
{
// 如果T为狗类,则typename T::type就是dog_tag,那么typename T::type()就是创建了一个dog_tag类的临时对象,根据函数重载的规则,这将调用Accept(T,dog_tag),这正是转向处理狗的策略。如果T为猫类,则typename T::type为cat_tag,由上面的推导,这将调用Accept(T,cat_tag),即转向处理猫的策略,typename 关键字告诉编译器T::type是个类型而不是静态成员。
Accept(animal, typename T::type()); // #1
}
所有类型推导,函数重载,都在编译期完成,你几乎不用耗费任何运行期成本(除了创建dog_tag,cat_tag临时对象的成本,然而经过编译器的优化,这种成本可能也会消失)就拥有了可读性和可维护性高的代码。“但是,等等!”你说:“traits在哪?”,typename T::type其实就是traits,只不过少了一层封装而已,如果像这样作一些改进:
template<typename T>
struct AnimalTraits
{
typedef T::type type;
};
于是,#1处的代码便可以写成:
Accept(animal, typename AnimalTraits<T>::type());
效率
通常为了提高效率,为某种情况采取特殊的措施是必要的,例如STL里面的copy,原型像这样:
// 将[first,last)区间内的元素拷贝到以dest开始的地方
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest){
// ptr_category用来萃取出迭代器的类别以进行适当程度的优化
return copy_opt(first,last,dest, ptr_category(first,dest));
}
copy_opt有两个版本,其中一个是针对如基本类型的数组作优化的,如果拷贝发生在char数组间,那么根本用不着挨个元素赋值,基于数组在内存中分布的连续性,可以用速度极快的memmove函数来完成。ptr_category有很多重载版本,对可以使用memmove的情况返回一个空类如scalar_ptr的对象以激发函数重载。其原始版本则返回空类non_scalar_ptr的对象。copy_opt的两个版本于是像这样:
// 使用memmove
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest,
scalar_ptr)
{ ...}
// 按部就班的逐个拷贝
template<typename IterIn,typename IterOut>
IterOut copy(IterIn first,IterIn last,IterOut dest,
non_scalar_ptr)
{ ...}
其实通常为了提高效率,还是需要分派。
使某些代码能通过编译
这或许令人费解,原来不能通过编译的代码,经过traits的作用就能编译了吗?是的,考虑std::pair的代码(为使代码简洁,忽略大部分):
template <typename T1, typename T2>
struct pair
{
T1 first;
T2 second;
// 如果T1或T2本身是引用,则编译错误,因为没有“引用的引用”
pair(const T1 & nfirst, const T2 & nsecond) // #2
:first(nfirst), second(nsecond) { }
};
这里可以使用一个traits(boost库里面的名字为add_reference)来避免这样的错误。这个traits内含一个typedef,如果add_reference<T>的T为引用,则typedef T type;如果不是引用,则typedef T& type;这样#2处的代码便可改成:
pair(add_reference<const T1>::type nfirst,
add_reference<const T2>::type nsecond)
...
这对所有的类型都能通过编译。
boost库中的traits
boost中的Traits十分完善,可分为如下几大类:
1. Primary Type Categorisation(初级类型分类)
2. Secondary Type Categorisation(次级类型分类)
3. Type Properties(类型属性)
4. Relationships Between Types(类型间关系)
5. Transformations Between Types(类型间转换)
6. Synthesizing Types(类型合成)
7. Function Traits(函数traits)
由于其中一些traits只是简单的模板偏特化,故不作介绍,本文仅介绍一些技术性较强的traits。由于traits的定义往往重复代码较多,所以必要时本文仅剖析其底层机制。所有源码均摘自相应头文件中,为使源码简洁,所有的宏均已展开。由于traits技巧与编译平台息息相关,某些平台可能不支持模板偏特化。这里我们假设编译器是符合C++标准的。在我的VC7.0上,以下代码均通过编译并正常工作。
初级类型分类
is_array (boost/type_traits/is_array.hpp)
定义
// 缺省
template<typename T>
struct is_array
{
static const bool value=false;
};
// 偏特化
template<typename T,size_t N>
struct is_array<T[N]>
{
static const bool value=true;
};
注解
C++标准允许整型常量表达式作为模板参数,上面的N就是这样。这也说明出现在模板偏特化版本中的模板参数(在本例中为typename T,size_t N两个)个数不一定要跟缺省的(本例中为typename T一个)相同,但出现在类名称后面的参数个数却要跟缺省的个数相同(is_array<T[N]>,T[N]为一个参数,与缺省的个数相同)。
使用
is_array<int [10]>::value // true(T=int,N=10)
is_array<int>::value // false(T=int)
is_class(.../is_class.hpp)
定义
// 底层实现,原因是根据不同的编译环境可能有不同的底层实现,我的编译环境为VC7.0,其他底层实现从略。
template <typename T>
struct is_class_impl
{
template <class U>
static ...::yes_type is_class_tester(void(U::*)(void));
template <class U> static ...::no_type is_class_tester(...);
// ice_and是一个元函数,提供逻辑与(AND)操作
border-right: medium none; padding-right: 0cm; border-top: medium none; padding-left: 0cm; background: #e6e6e6; padding-bottom: 0cm; margin: 0cm 0cm 0pt; border-left: medium none; text-indent: 24
分享到:
相关推荐
在IT行业中,C++是一种强大的系统级编程语言,而Lua则是一种轻量级的脚本语言,常用于游戏开发、嵌入式系统以及各种应用程序的扩展。Kaguya是C++的一个lua绑定库,它使得C++程序可以方便地集成lua代码,实现两者之间...
Boost Graph Library是一个功能强大的图形处理工具包,它不仅提供了丰富的图形算法和数据结构,还采用了先进的泛型编程技术,使开发者能够方便地解决复杂的图形计算问题。通过学习和掌握BGL,程序员可以在多个领域中...
#ifndef __TYPE_TRAITS_H #include <type_traits.h> #endif #include #include #include #include #include #ifdef __STL_USE_NEW_IOSTREAMS #include #else /* __STL_USE_NEW_IOSTREAMS */ #include #...
callable_traits, callable类型的现代 C 型特征和泛函 Boost.CallableTraits CallableTraits是一个C 11头库,用于检查。合成和分解可以调用类型。这里有最新的文档,这里是 。在CallableTraits中,被正式地检查过,...
这本书不适合C++ 初学者,不适合 Genericity(泛型技术)初学者,或 STL 初学者。这本书也不适合带领你学习面向对象(Object Oriented)技术 — 是的,STL 与面向对象没有太多关连。本书前言清楚说明了书籍的定位和...
2. 模板元编程:`boost::type_traits`库提供了大量用于检查和操作类型的模板类,这对于编写类型安全的代码非常有用。 3. 容器和算法:`boost::container`提供了各种容器,如`boost::unordered_map`,作为C++标准库...
4. **泛型编程** (Generic Programming):Boost.MPL(Metaprogamming Library)和Boost.TypeTraits库提供了元编程工具,允许在编译时进行计算和检查类型属性。 5. **并发与线程** (Concurrency and Threads):Boost...
7. **泛型编程**:如mpl(Metaprogamming Library)和type_traits,它们允许在编译时进行计算和类型检查。 8. **算法元编程**:如boost::algorithm,它提供了一套用于字符串处理的算法,如is_all_equal和iota。 9....
7. **类型识别与转换**:`boost.type_traits`库提供了各种类型属性查询和类型转换工具,用于实现模板元编程或类型安全的代码。 8. **容器与迭代器**:虽然Boost不直接与STL竞争,但有如`boost::ptr_vector`这样的...
8. **泛型编程和元编程**:`mpl`(Meta-Programming Library)提供了编译时编程的工具,`type_traits`则提供了类型属性检查和转换。 9. **序列化库**:`serialization`库允许将对象的状态保存到文件并恢复,支持XML...
资源分类:Python库 所属语言:Python 资源全名:link_traits-1.0.3.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
### C++ Traits:类型属性封装与偏特化 在C++中,`Traits`类是一种非常重要的设计模式,它主要用于封装类型的各种属性。...总之,掌握`Traits`类的设计与使用是每个C++程序员都应该具备的重要技能之一。
- **泛型编程**:利用Boost库中的元编程技术(如`type_traits`)来进行泛型编程,提高代码的灵活性和重用性。 #### 六、Boost库的源码分析 - **容器赋值(assign)**:深入理解容器赋值的底层实现,包括`assign`等...
8. **泛型编程库**:Boost.MPL和Boost.TypeTraits是泛型编程的重要工具,帮助开发者编写更高效、更具扩展性的代码。 9. **预处理库**:Boost.Preprocessor提供了一套宏系统,可以在编译时进行代码生成。 10. **...
在C++编程语言中,`std::common_type`是一个非常重要的工具,用于处理不同类型的混合操作,特别是当我们在模板编程或者泛型算法中需要统一不同类型的操作时。`std::common_type`可以推导出两个或多个类型之间的共同...
### STL源码剖析知识点梳理 #### 一、STL概览 - **STL**(Standard Template Library,标准模板库)是C++标准库的重要组成部分,它提供了丰富的数据结构和算法,极大地提高了程序开发的效率。 - **SGI STL**...