boost源码剖析之:泛型编程精灵type_traits(rev#2)

本文深入剖析了boost源码中type_traits的泛型编程。介绍了使用traits的三种动机:分派、效率和使代码通过编译,并给出具体示例。还阐述了boost库中traits的分类,如初级类型分类、次级类型分类等,且对初级类型分类中的is_array和is_class进行了定义、注解和使用说明。

boost源码剖析之:泛型编程精灵type_traits(rev#2)

刘未鹏

C++的罗浮宫(http://blog.youkuaiyun.com/pongba)

动机

使用traits的动机一般有三种,分派、效率、使某些代码通过编译。

分派

下面有一个模板函数,假设一个动物收容组织提供了它,他们接受所有无家可归的可怜的小动物,于是他们向外界提供了一个函数接受注册。函数看起来像这样:

template<class T> // T表示接受的是何种动物

void AcceptAnimals(T animal)

{

... //do something

};

但是,如果他们想将猫和狗分开处理(毕竟饲养一只猫和饲养一只狗并不相同。他们可能会为狗买一根链子,而温顺的猫则可能不需要)。一个可行的方法是分别提供两个函数:AcceptDogAcceptCat,然而这种解决办法并不优雅(想想看,注册者可能既有一只猫又有一只狗,这样他不得不调用不同的函数来注册,而且,如果种类还在增多呢,那样会导致向外提供的接口的增多,注册者因此而不得不记住那些烦琐的名字,而这显然没有只需记住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::typecat_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;

// 如果T1T2本身是引用,则编译错误,因为没有“引用的引用

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)操作

内容概要:本文为《科技类企业品牌传播白皮书》,系统阐述了新闻媒体发稿、自媒体博主种草与短视频矩阵覆盖三大核心传播策略,并结合“传声港”平台的AI工具与资源整合能力,提出适配科技企业的品牌传播解决方案。文章深入分析科技企业传播的特殊性,包括受众圈层化、技术复杂性与传播通俗性的矛盾、产品生命周期影响及2024-2025年传播新趋势,强调从“技术输出”向“价值引领”的战略升级。针对三种传播方式,分别从适用场景、操作流程、效果评估、成本效益、风险防控等方面提供详尽指南,并通过平台AI能力实现资源智能匹配、内容精准投放与全链路效果追踪,最终构建“信任—种草—曝光”三位一体的传播闭环。; 适合人群:科技类企业品牌与市场负责人、公关传播从业者、数字营销管理者及初创科技公司创始人;具备一定品牌传播基础,关注效果可量化与AI工具赋能的专业人士。; 使用场景及目标:①制定科技产品全生命周期的品牌传播策略;②优化媒体发稿、KOL合作与短视频运营的资源配置与ROI;③借助AI平台实现传播内容的精准触达、效果监测与风险控制;④提升品牌在技术可信度、用户信任与市场影响力方面的综合竞争力。; 阅读建议:建议结合传声港平台的实际工具模块(如AI选媒、达人匹配、数据驾驶舱)进行对照阅读,重点关注各阶段的标准化流程与数据指标基准,将理论策略与平台实操深度融合,推动品牌传播从经验驱动转向数据与工具双驱动。
【3D应力敏感度分析拓扑优化】【基于p-范数全局应力衡量的3D敏感度分析】基于伴随方法的有限元分析和p-范数应力敏感度分析(Matlab代码实现)内容概要:本文档围绕“基于p-范数全局应力衡量的3D应力敏感度分析”展开,介绍了一种结合伴随方法与有限元分析的拓扑优化技术,重点实现了3D结构在应力约束下的敏感度分析。文中详细阐述了p-范数应力聚合方法的理论基础及其在避免局部应力过高的优势,并通过Matlab代码实现完整的数值仿真流程,涵盖有限元建模、灵敏度计算、优化迭代等关键环节,适用于复杂三维结构的轻量化与高强度设计。; 适合人群:具备有限元分析基础、拓扑优化背景及Matlab编程能力的研究生、科研人员或从事结构设计的工程技术人员,尤其适合致力于力学仿真与优化算法开发的专业人士; 使用场景及目标:①应用于航空航天、机械制造、土木工程等领域中对结构强度和重量有高要求的设计优化;②帮助读者深入理解伴随法在应力约束优化中的应用,掌握p-范数法处理全局应力约束的技术细节;③为科研复现、论文写作及工程项目提供可运行的Matlab代码参考与算法验证平台; 阅读建议:建议读者结合文中提到的优化算法原理与Matlab代码同步调试,重点关注敏感度推导与有限元实现的衔接部分,同时推荐使用提供的网盘资源获取完整代码与测试案例,以提升学习效率与实践效果。
### `vector` 中的 `typedef` 定义及其作用 在 C++ STL 的 `vector` 源码中,`typedef` 被广用于简化复杂的类表达,并增强代码的可读性和可维护性。以下是对两个典 `typedef` 行的解释: ```cpp typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type; typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer pointer; ``` #### 1. `typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;` 这一行的作用是定义一个新的分配器类 `_Tp_alloc_type`,它是基于原始分配器 `_Alloc` 重新绑定到元素类 `_Tp` 的结果。 - `__gnu_cxx::__alloc_traits` 是 GNU C++ 标准库实现中的一个模板类,用于封装分配器的行为和特性。 - `rebind<_Tp>` 是 `__alloc_traits` 提供的一个机制,允许将当前分配器类转换为适用于另一种类的分配器。例如,如果 `_Alloc` 是 `std::allocator<int>`,则通过 `rebind<double>` 可以得到 `std::allocator<double>`。 - `other` 是 `rebind` 的结果类别名。 - `typename` 和 `template` 关键字用于在模板上下文中正确解析嵌套依赖类。 最终,`_Tp_alloc_type` 成为了适用于当前 `vector` 元素类的分配器类,这为后续内存操作提供了类支持[^4]。 #### 2. `typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer pointer;` 这一行定义了一个指针类别名 `pointer`,它表示由 `_Tp_alloc_type` 所管理的指针类。 - `__alloc_traits` 中定义了多个类成员,包括 `pointer`、`const_pointer`、`reference` 等。 - 此处的 `pointer` 类通常等价于 `_Tp*`,但在某些自定义分配器中可能被重载为智能指针或其他形式的“指针包装”。 - 使用 `typedef` 将其定义为 `pointer`,使得 `vector` 内部可以统一使用该类进行元素访问和操作,而无需关心底层分配器是否改变了指针语义。 这种抽象方式增强了 `vector` 对不同分配器的兼容性,同时也提升了代码的可移植性和灵活性[^4]。 --- ### 示例:简化后的分配器绑定与指针类定义 ```cpp #include <memory> template<typename T, typename Alloc = std::allocator<T>> class my_vector { using alloc_traits = std::allocator_traits<Alloc>; using Tp_alloc_type = typename alloc_traits::template rebind_alloc<T>; using pointer = typename alloc_traits::pointer; // ... }; ``` 此示例展示了如何利用标准库中的 `allocator_traits` 来完成类似的类绑定与抽象,体现了现代 C++ 对分配器模的支持与封装策略[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值