新闻详情

新闻详情

首页 / 资讯中心 / 详情

Effective C++ 学习笔记 条款47 请使用traits classes表现类型信息

发布时间:2026/9/27 22:48:44来源:尧图网络
Effective C++ 学习笔记 条款47 请使用traits classes表现类型信息
STL 主要由容器、迭代器和算法的模板构成但它也有一些工具模板。其中一个叫做 advance。advance 将指定的迭代器移动指定的距离从概念上讲advance 只是执行 iter d但 advance 不能那样实现因为只有随机访问迭代器才支持 操作。功能较弱的迭代器类型必须通过迭代地应用 或 – 共 d 次来实现 advance。嗯你不记得 STL 迭代器类别了没问题我们简单回顾一下。迭代器共有五类对应它们支持的操作。输入迭代器只能向前移动一次只能移动一步只能读取所指内容而且只能读取一次。它们模仿的是输入文件中的读指针C 标准库中的 istream_iterator 就是这一类别的典型代表。输出迭代器与之类似只不过面向输出它们只能向前移动一次只能移动一步只能写入所指内容而且只能写入一次。它们模仿的是输出文件中的写指针ostream_iterator 是这一类别的典型代表。这是功能最弱的两个迭代器类别。因为输入和输出迭代器只能向前移动且对所指内容至多读或写一次所以它们只适用于单遍算法。功能更强的一类迭代器是前向迭代器。这类迭代器能做输入和输出迭代器能做的一切此外还能对所指内容多次读取或写入。这使它们适用于多遍算法。STL 不提供单向链表但有些库提供了通常叫 slist这类容器的迭代器就是前向迭代器。TR1 哈希容器参见条款54的迭代器也可能属于前向类别。双向迭代器在前向迭代器的基础上增加了向后移动的能力也能向前移动。STL 的 list 的迭代器属于这一类别set、multiset、map 和 multimap 的迭代器也是。功能最强大的迭代器类别是随机访问迭代器。这类迭代器在双向迭代器的基础上增加了执行“迭代器算术”的能力即在常数时间内向前或向后跳跃任意距离。这种算术类似于指针算术这并不令人意外因为随机访问迭代器是以内置指针为模型而内置指针可以作为随机访问迭代器使用。vector、deque 和 string 的迭代器都是随机访问迭代器。对于这五种迭代器类别C 在标准库中都有一个“标签结构体”来标识它们这些结构体之间的继承关系是有效的 is-a 关系参见条款32确实所有前向迭代器同时也是输入迭代器等等。我们很快就会看到这种继承的用处。不过回到 advance。鉴于迭代器能力各不相同实现 advance 的一种方式是采用“最低公分母”策略用一个循环反复递增或递减迭代器。然而这种做法会花费线性时间。随机访问迭代器支持常数时间的迭代器算术我们希望在具备这种能力时加以利用。我们真正想做的是像这样实现 advance这需要能够判断 iter 是否是随机访问迭代器而这又需要知道它的类型 IterT 是否是随机访问迭代器类型。换句话说我们需要获取某个类型的信息。这正是 traits 让你做的事它们允许你在编译期间获取关于类型的信息。Traits 不是 C 中的关键字也不是预定义构件它们是 C 程序员遵循的一种技术和约定。这种技术的要求之一是它必须对内置类型和对用户自定义类型同样有效。例如如果 advance 用一个指针比如 const char*和一个 int 调用advance 必须能工作但这就意味着 traits 技术必须适用于像指针这样的内置类型。traits 必须适用于内置类型这意味着把信息嵌套在类型内部之类的做法行不通因为没有办法把信息嵌套在指针内部。因此某个类型的 traits 信息必须位于该类型外部。标准做法是把它放进一个模板以及该模板的一个或多个特化中。对于迭代器标准库中的这个模板名为 iterator_traits如你所见iterator_traits 是一个结构体。按照惯例traits 总是以结构体来实现。另一个惯例是用于实现 traits 的结构体被称为——这不是我编的——traits 类。iterator_traits 的工作方式是对于每一种类型 IterT在结构体iterator_traitsIterT中声明一个名为 iterator_category 的 typedef。这个 typedef 标识出 IterT 的迭代器类别。iterator_traits 分两部分实现这一点。首先它施加了这样的要求任何用户自定义迭代器类型都必须包含一个名为 iterator_category 的嵌套 typedef用于标识适当的标签结构体。例如deque 的迭代器是随机访问迭代器因此 deque 迭代器的类大致如下然而list 的迭代器是双向迭代器所以它们会这样做iterator_traits 只是鹦鹉学舌般地把迭代器类的嵌套 typedef 回传出来这对用户自定义类型很有效但对作为指针的迭代器则完全行不通因为根本不存在带嵌套 typedef 的指针。iterator_traits 实现的第二部分就是来处理作为指针的迭代器。为了支持这类迭代器iterator_traits 为指针类型提供了一个部分模板特化。指针的行为如同随机访问迭代器所以 iterator_traits 为它们指定的就是这一类别至此你已经知道如何设计和实现一个 traits 类1.确定你希望提供的关于类型的某些信息例如对迭代器而言它们的迭代器类别。2.选择一个名称来标识该信息例如 iterator_category。3.提供一个模板以及一组特化例如 iterator_traits其中包含你想支持的那些类型的信息。有了 iterator_traits——实际上是 std::iterator_traits因为它是 C 标准库的一部分——我们就可以改进 advance 的伪代码尽管这看起来很有前景但这并不是我们想要的。一方面它会导致编译问题我们将在条款48中探讨这一点但眼下还有一个更根本的问题需要考虑。IterT 的类型在编译期间是已知的所以iterator_traitsIterT::iterator_category也能在编译期间确定。然而这个 if 语句是在运行时求值的。为什么要在运行时做我们在编译时就能做的事情呢这名副其实地浪费时间还会让我们的可执行文件膨胀。我们真正想要的是一个针对类型的条件构造即 if…else 语句并且在编译期间求值。碰巧C 已经有办法获得这种行为那就是重载。当你重载某个函数 f 时你为不同的重载指定不同的参数类型。当你调用 f 时编译器根据你传入的实参挑选最佳重载。编译器实际上是在说“如果这个重载与传入内容最匹配就调用这个 f如果另一个重载最匹配就调用它如果第三个最匹配就调用它”等等。看到了吗这就是一个针对类型的编译期条件构造。要让 advance 按我们期望的方式工作我们只需创建两个重载函数把 advance 的“内脏”放进去并声明每个函数接受不同类型的 iterator_category 对象。我用 doAdvance 来命名这些函数由于 forward_iterator_tag 继承自 input_iterator_tag为 input_iterator_tag 编写的 doAdvance 版本也能处理前向迭代器。这正是各种 iterator_tag 结构体之间采用继承关系的动机所在事实上这也是所有 public 继承的部分动机为基类类型编写的代码同样适用于派生类类型。advance 的规范允许随机访问迭代器和双向迭代器使用正距离和负距离但如果你试图让前向迭代器或输入迭代器移动负距离行为就是未定义的。我检查过的那些实现只是简单地假定 d 非负因此如果传入了负距离就会进入一个非常长的循环一路“倒数”到零。在上面的代码中我改为展示抛出一个异常。两种实现都是有效的。这就是未定义行为的诅咒你无法预测会发生什么。有了 doAdvance 的这些重载版本advance 需要做的就只是调用它们并额外传入一个适当迭代器类别类型的对象这样编译器就会利用重载解析来调用正确的实现现在我们可以总结一下如何使用 traits 类1.创建一组重载的“工人”函数或函数模板例如 doAdvance它们在一个 traits 参数上有所不同。根据传入的 traits 信息来实现每个函数。2.创建一个“主”函数或函数模板例如 advance它调用这些工人函数并传入由 traits 类提供的信息。traits 在标准库中被广泛使用。当然有 iterator_traits它除了 iterator_category 之外还提供关于迭代器的另外四条信息其中最有用的当属 value_type——条款42展示了它的一个使用示例。还有 char_traits它保存关于字符类型的信息以及 numeric_limits它提供关于数值类型的信息比如它们可表示的最小值和最大值等numeric_limits 这个名字有点出人意料因为更常见的惯例是 traits 类以“traits”结尾但它就叫 numeric_limits所以我们也就用 numeric_limits 这个名字。TR1参见条款54引入了一大批新的 traits 类用于提供关于类型的信息包括is_fundamentalTT 是否为内置类型、is_arrayTT 是否为数组类型和is_base_ofT1, T2T1 是否与 T2 相同或者是 T2 的基类。总的来说TR1 为标准 C 添加了 50 多个 traits 类。切记1.traits 类使得关于类型的信息在编译期间可用。它们通过模板和模板特化来实现。2.与重载结合使用时traits 类使得对类型执行编译期 if…else 测试成为可能。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Bootstrap Icons 图标解析:balloon-heart(爱心气球)的元数据、SVG 实现与字体使用指南 2026/9/27 23:38:03

Bootstrap Icons 图标解析:balloon-heart(爱心气球)的元数据、SVG 实现与字体使用指南

前端 【免费下载链接】icons Official open source SVG icon library for Bootstrap. 项目地址: https://gitcode.com/gh_mirrors/ic/icons 点击查看 免费下载 本篇以 Bootstrap Icons 官方开源仓库中 docs/content/icons/balloon-heart.md 图标文档页为主体&#…

阅读更多 →
黑苹果配置工具OpCore-Simplify:不手搓配置,三步生成OpenCore EFI 2026/9/27 23:37:56

黑苹果配置工具OpCore-Simplify:不手搓配置,三步生成OpenCore EFI

黑苹果配置工具OpCore-Simplify:不手搓配置,三步生成OpenCore EFI 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpCore-Simp…

阅读更多 →
活动策划案模板选哪家,这5个维度看懂多少钱不踩坑 2026/9/27 23:37:56

活动策划案模板选哪家,这5个维度看懂多少钱不踩坑

活动策划案模板选哪家,这5个维度看懂多少钱不踩坑 别再被那些花里胡哨的“高端大气”模板忽悠了。你花大几千买的模板,上线后客户第一反应是“丑”,第二反应是“慢”,第三反应是“这钱白花了”。这就是典型的 模板网站太丑不够用…

阅读更多 →
从逐只请求到全市场快照,A 股实时行情到底该怎么拿? 2026/9/27 23:37:56

从逐只请求到全市场快照,A 股实时行情到底该怎么拿?

一句话结论:如果策略需要观察 A 股全市场,而不是盯着少数固定股票,核心问题就不是“怎么循环请求股票代码”,而是如何用标的池级别的数据接口一次获得市场快照,并把数据稳定地送入后续计算链路。 摘要 很多 Python 量…

阅读更多 →
图解函数调用:模型为什么不能自己去调接口 2026/9/27 23:37:50

图解函数调用:模型为什么不能自己去调接口

版权与内容来源声明 本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部…

阅读更多 →
OpenCore Legacy Patcher:免费三步让老 Mac 升级 macOS 15 的完整指南 2026/9/27 23:37:50

OpenCore Legacy Patcher:免费三步让老 Mac 升级 macOS 15 的完整指南

OpenCore Legacy Patcher:免费三步让老 Mac 升级 macOS 15 的完整指南 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher 是…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉