C++ Traits Classes
参考博文 https://blog.csdn.net/lihao21/article/details/55043881
Traits classes 的作用主要是用来为使用者提供类型信息。在 C++ 中,traits 习惯上总是被实现为 struct ,但它们往往被称为 traits classes。
为了清晰理解 traits 的原理,我们先来看 traits 使用的关键技术 —— 模板的特化与偏特化。
模板特化(Template Specialization)
所谓特化,就是将泛型的东西搞得具体化一些,从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,受到特定的修饰,或完全被指定了下来。
我们先来看下一函数模板的通用定义:
template<typename T>
struct my_is_void {
static const bool value = false;
};
然后,针对 void 类型,我们有以以下的特化版本:
template<>
struct my_is_void<void> {
static const bool value = true;
};
测试代码如下:
my_is_void<bool> t1;
cout << t1.value << endl; // 输出0
my_is_void<void> t2;
cout << t2.value << endl; // 输出1
当声明 my_is_void<void> t2; 时,使用的是特化版本,故其 value 值为 1。
偏特化(Patial Spcialization)
偏特化就是只指定一部分而非所有模板参数,或者是参数的一部分而非全部特性。(模板函数只能全特化,没有偏特化)
template<typename T>
struct my_is_pointer {
static const bool value = false;
};
我们对模板参数T进行限制,要求其为一个指针的类型:
template<typename T>
struct my_is_pointer<T*> {
static const bool value = true;
};
测试:
my_is_pointer<int> p1;
cout << p1.value << endl; // 输出 0,使用原始模板
my_is_pointer<int*> p2;
cout << p2.value << endl; // 输出 1,使偏特化模板,因为指定了 int * 类型的参数
typename
以下模板的声明中, class 和 typename 有什么不同?
template<class T> class Test;
template<typename T> class Test;
答案是没有不同。但除此之外,C++ 并不总是把 class 和 typename 视为等价。有时候我们一定得使用 typename。
默认情况下,C++ 语言假定通过作用域运算符访问的名字不是类型。因此,如果你希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。我们通过使用关键字 typename 来实现这一点:
template<typename T>
typename T::value_type top(const T &c)
{
if (!c.empty())
return c.back();
else
return typename T::value_type();//必须加typename
}
top 函数期待一个容器类型的实参,它使用 typename 指明其返回类型
测试代码:
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
cout << top<vector<int> >(vec) << endl; // 输出3
当我们希望通知编译器一个名字表示类型时,必须使用关键字 typename,而不能使用 class。
Traits Classes
说完了背景知识,我们正式进入 traits
我们知道,在 STL 中,容器与算法是分开的,彼此独立设计,容器与算法之间通过迭代器联系在一起。那么,算法是如何从迭代器类中萃取出容器元素的类型的?没错,这正是我们要说的 traits classes 的功能。
迭代器所指对象的类型,称为该迭代器的 value_type。我们来简单模拟一个迭代器 traits classes 的实现。
template<class IterT>
struct my_iterator_traits {
typedef typename IterT::value_type value_type;
};
my_iterator_traits 其实就是个类模板,其中包含一个类型的声明。
对于my_iterator_traits,我们再声明一个偏特化版本。
template<class IterT>
struct my_iterator_traits<IterT*> {
typedef IterT value_type;
//即如果 my_iterator_traits 的实参为指针类型时,
//直接使用指针所指元素类型作为 value_type。
};
为了测试 my_iterator_traits 能否正确萃取迭代器元素的类型,我们先编写以下的测试函数。
void fun(int a) {
cout << "fun(int) is called" << endl;
}
void fun(double a) {
cout << "fun(double) is called" << endl;
}
void fun(char a) {
cout << "fun(char) is called" << endl;
}
我们通过函数重载的方式,来测试元素的类型。
测试代码如下:
my_iterator_traits<vector<int>::iterator>::value_type a;
fun(a); // 输出 fun(int) is called
my_iterator_traits<vector<double>::iterator>::value_type b;
fun(b); // 输出 fun(double) is called
my_iterator_traits<char*>::value_type c;
fun(c); // 输出 fun(char) is called
为了便于理解,我们这里贴出 vector 迭代器声明代码的简化版本:
template <class T, ...>
class vector {
public:
class iterator {
public:
typedef T value_type;
...
};
...
};
我们来解释 my_iterator_traits<vector<int>::iterator>::value_type a; 语句的含义:
vector<int>::iterator 为 vector<int> 的迭代器,该迭代器包含了 value_type 的声明,由 vector 的代码可以知道该迭代器的 value_type 即为 int 类型。
接着,my_iterator_traits<vector<int>::iterator> 会采用 my_iterator_traits 的通用版本,即 my_iterator_traits<vector<int>::iterator>::value_type 使用 typename IterT::value_type 这一类型声明,这里 IterT 为 vector<int>::iterator,故整个语句萃取出来的类型为 int 类型。
对 double 类型的 vector 迭代器的萃取也是类似的过程。
而 my_iterator_traits<char*>::value_type 则使用 my_iterator_traits 的偏特化版本,直接返回 char 类型。
由此看来,通过 my_iterator_traits ,我们正确萃取出了迭代器所指元素的类型。
总结一下我们设计并实现一个 traits class 的过程:
- 确认若干我们希望将来可取得的类型相关信息,例如,对于上面的迭代器,我们希望取得迭代器所指元素的类型;
- 为该信息选择一个名称,例如,上面我们起名为 value_type;
- 提供一个 template 和一组特化版本(例如,我们上面的 my_iterator_traits),内容包含我们希望支持的类型相关信息。
C++ Traits Classes的更多相关文章
- Effective C++ -----条款47:请使用traits classes表现类型信息
Traits classes使得“类型相关信息”在编译期可用.它们以template和“templates特化”完成实现. 整合重载技术(overloading)后,traits classes有可能 ...
- Effective C++ Item 47 请使用 traits classes 表现类型信息
本文为senlie原创.转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:Traits classes 使得"类型相关信息"在编译期可用.它 ...
- 【47】请使用traits classes表现类型信息
1.考虑下面的需求,对迭代器移动d个单位.因为不同类型的迭代器,能力不同,有的迭代器(vector,deque内置迭代器)可以一步到位移动到指定位置,有的迭代器(list内置迭代器)必须一步一步移动, ...
- 读书笔记_Effective_C++_条款四十七:请使用trait classes来表示类型信息
这一条款主要来讨论模板中迭代器的属性iterator_category,它可以通过类似于vector<int>::iterator::iterator_category的方式来取得. 到这 ...
- 侯捷C++ Type traits(类型萃取
泛型編程編出來的代碼,適用於任何「吻合某種條件限制」的資料型別.這已成為撰寫可復用代碼時的一個重要選擇.然而,總有一些時候,泛型不夠好 — 有時候是因為不同的型別差距過大,難以產生一致的泛化實作版本. ...
- C++中的Traits技法
Traits广泛应用于标准程序库.Traits classes使得"类型相关信息"在编译期可用. 认真读完下面的示例,你应该就懂了Traits技法,其实并不难. #include ...
- 读书笔记 effective c++ Item 47 使用traits class表示类型信息
STL主要由为容器,迭代器和算法创建的模板组成,但是也有一些功能模板.其中之一叫做advance.Advance将一个指定的迭代器移动指定的距离: template<typename IterT ...
- 《Effective C++》再次探索traits技法
首先介绍C++标准程序库中的五种迭代器,关于这个可以看我的另一个笔记:http://blog.csdn.net/m0_37316917/article/details/70053513. 对于这五种分 ...
- Beginning Scala study note(3) Object Orientation in Scala
1. The three principles of OOP are encapsulation(封装性), inheritance(继承性) and polymorphism(多态性). examp ...
- 《Effective C++》第三版笔记
阅读此笔记前,请先阅读 <Effective C++>第二版笔记 和 <More Effective C++>笔记 这里只记录与上面笔记不同的条款,主要是 "面对 ...
随机推荐
- Vue2系列(lqz)——6-Vue-cli、7-Vue插件、8-Vue第三方框架之ElementUi
文章目录 6 Vue-CLI 项目搭建 1 单文件组件 2 Vue-CLI 项目搭建 2.1 环境搭建 2.2 项目的创建 创建项目 启动/停止项目 打包项目 package.json中 2.3 认识 ...
- 使用yum管理RPM软件包
yum概念 对比rpm命令,rpm命令需要手动寻找安装该软件包所需要的一系列依赖关系.当软件包需要卸载时,容易由于卸载掉了某个依赖关系而导致其他的软件包不能用. yum(Yellow dog upda ...
- ChatGPT API FAQ
ChatGPT API FAQ General questions about the ChatGPT API Written by Johanna C.. Updated over a week a ...
- 聊聊 RocketMQ 消息轨迹
这篇文章,我们聊一聊 RocketMQ 的消息轨迹设计思路. 查询消息轨迹可作为生产环境中排查问题强有力的数据支持 ,也是研发同学解决线上问题的重要武器之一. 1 基础概念 消息轨迹是指一条消息从生产 ...
- 带您了解 O2OA 流程中的人工活动处理方式
这次咱们来介绍 O2OA (翱途) 开发平台流程引擎中的人工活动的处理方式和逻辑,O2OA (翱途) 主要采用拖拽可视化开发的方式完成流程的设计和配置,不需要过多的代码编写,业务人员可以直接进行修改操 ...
- 记一次 .NET 某工控电池检测系统 卡死分析
一:背景 1. 讲故事 前几天有位朋友找到我,说他的窗体程序有卡死现象,让我帮忙看下怎么回事,解决这种问题就需要在卡死的时候抓一个dump下来,拿到dump之后就可以分析了. 二:为什么会卡死 1. ...
- HarmonyOS 实战项目
引言 本章将介绍如何在 HarmonyOS 上进行实际项目开发.我们将从项目需求分析开始,逐步完成项目的设计.开发.测试和上线过程. 目录 项目需求分析 项目设计 项目开发 项目测试 项目上线 总结 ...
- goto关键词
1.前言 goto,一个蒟蒻一用就废,大佬一用就吊炸天的神奇关键字. 今天,我要来盘它!!! 2.goto只能在函数内实现跳转,不能跨函数跳转 因为标号label是局部有效的. #include &l ...
- [NewStarCTF WEEK5] pwn-planet 详解
这道题目更多是考pwner的逆向功底(虽然程序逻辑也不是非常复杂=_=) 老规矩,先checksec查看程序 保护全开 看一下main函数 __int64 __fastcall main(int a1 ...
- 论文阅读:2023_Semantic Hearing: Programming Acoustic Scenes with Binaural Hearables
论文地址:语义听觉:用双耳可听器编程声学场景 论文代码:https://semantichearing.cs.washington.edu/ 引用格式:Veluri B, Itani M, Chan ...