C++ std::ranges::in_found_result

2025年2月11日 | 阅读 9 分钟

C++ 经历了多次改动并增加了更多功能,这反过来使该语言更加灵活。这是近年来最显著的改进之一;ranges 是 C++20 的全新概念。Ranges 是思考数据序列的更逻辑、更可用的方式;它们将数据结构(如数组vector)以及对它们的操作封装到一个单一的实体中。这种演变改变了开发人员处理数据集合的方式,使代码更具可读性和可写性,更高效,并且不易出错。

在引入 ranges 之前,使用 C++ 中的迭代器操作序列主要需要大量的前置迭代器操作,这使得生成的代码庞大且容易出错。例如,让我们看一个我们要在列表中实现的简单操作。没有 ranges,这可能需要编写直接操作迭代器的代码,这可能不太灵活和直观,尤其是在一个人不太熟悉 C++ 的情况下。

  • Ranges 的出现改变了这种思维方式。使用 ranges,您可以将一个 range 作为值传递,而不必担心保存该 range 的集合的具体实现是什么。这不仅消除了复杂的编码术语,还提高了代码的语义/含义和目的。
  • 例如,与其将begin()end()迭代器传递给算法,不如传递 range,而后者已经包含了两者。支持程序的代码量减少了;因此,程序本身更容易阅读、编写和调试。
  • 在描述 C++ 中的 ranges 和算法时,另一个需要讨论的问题是这些算法可以提供的返回类型范围。在提供算法的返回类型时,需要了解算法发生的事件结果。例如,在搜索范围中的元素时,返回类型可以告诉搜索者元素是否被找到,如果找到了,则从哪里找到。

什么是 in_found_result?

in_found_result 类型是在 ranges 和 C++ 标准库中一些与范围搜索相关的过程中使用的一种返回类型。该类型旨在包含算法的结果,该算法除了返回元素的位置之外,还需要携带一些额外的信息。

在详细讨论 in_found_result 之前,应解释其功能和组成部分。该类型的主要目的是在执行涉及元素搜索的操作时,获得更模糊、更有用的答案。例如,考虑 ranges::find 算法。此算法查找范围中的确定数量,并且必须提供有关数量是否找到以及如果找到,在哪里找到的信息。

传统上,像 std::find 这样的算法会在元素所在的位置返回一个迭代器。否则,它会在范围的末尾返回一个迭代器。虽然这可以工作,但在某些情况下可能有些受限。ranges 库引入的 in_found_result 类型通过返回一个更具表现力的结果来增强这一点,该结果包含两个关键信息:

  • In:这里是我们表示输入迭代器的成员。它指向范围中搜索的最后一个点。如果找到了元素,它将包含一个属性 in,指向找到的元素的位置。如果未在范围中找到,则它将指向范围的末尾。
  • Found:此成员是布尔值,如果搜索成功则设置为 true;否则为 false。如果是,则表示元素实际上在给定范围内找到了。如果 found 为 false,则表示未找到元素;我们需要返回 false。

这两个成员共同提供了比单个迭代器更强大、更丰富的结果,在 in_found_result 类型推断的情况下。它使得开发人员能够立即获得搜索结果,而无需手动与范围末尾进行比较。

C++ 中 in_found_result 的用例

在 C++ 接口中,当您想要有关范围内的搜索操作的更详细信息时,in_found_result 返回类型可能会更有帮助。以下是 in_found_result 可以有效应用的几个关键场景:

1. 根据搜索进行条件操作

在范围内搜索以查找元素时,大多数情况下会关联一些条件操作。例如,如果找到一个元素,通常希望更改它或进行更多操作。在元素缺失时用空白或下划线标记是一个好主意,或者可能希望插入它。in_found_result 通过返回一个布尔值(如果找到元素则为 true,并返回到该位置的迭代器,否则返回 null)使这更加简单。

2. 避免冗余操作

为了在传统 C++ 搜索算法中实现后者,执行搜索后,必须将返回的迭代器与搜索范围的最后一个元素进行比较,以确定是否找到该元素。这反过来又引入了更多的工作、时间和出错的可能性。在 in_found_result 中,搜索结果包含在此信息中,因此无需重复比较,从而使代码干净且无错误。

3. 增强的调试和日志记录

能够立即获得搜索结果和搜索停止的位置通常非常有用。in_found_result:在搜索操作失败的情况下,它允许您记录搜索停止的确切时刻,从而可以更详细地评估程序的行为。在数据流通过算法的分析至关重要的系统中,这可能尤为重要。

示例

输出

 
Element 40 found at position: 3 
Value doubled. New value: 80 
Final vector contents: 10 20 30 80 50 60    

说明

  • 初始设置:首先,我们需要定义一个长度为六的整数 vector。然后,我们给出一个我们希望在 vector 中查找的值(value_to_find = 40)。
  • 搜索操作:通过使用 std::ranges::find 进行搜索,如果元素存在于范围中,该函数将返回指向该元素的迭代器。否则,它将返回指向范围末尾的迭代器。
  • 处理结果:然后将上述结果包装在 in_found_result 对象中,该对象还包括一个布尔类型(found)以指示是否找到了元素。
  • 条件逻辑:根据是否找到元素
    • 如果找到代码,则将代码标识的元素的值乘以二。
    • 如果未找到,则将其放置在 vector 的最后一个位置。
  • 最终输出:最后,代码打印元素的位置(如果找到),或修改后的 vector 或包含新元素的更新后的 vector。

使用 in_found_result 的优点

1. 增强代码清晰度

这是因为 C++ 的 in_found_result 等功能清晰明了,并增加了代码的清晰度。处理搜索后值的一般技术是使用迭代器,通过与范围的末尾进行比较来查看是否找到了元素。这会使代码相当难以阅读,尤其是在 C++ 新手或处理大型项目时。in_found_result 包含迭代器和布尔值,它们比仅有迭代器具有更多的解释性,使代码一眼就能轻松理解。

2. 减少错误的可能性

因此,in_found_result 通过将迭代器和成功指示器打包到一个返回类型中,提供了免受多种潜在错误的影响。在传统 C++ 代码中,经常使用迭代器对象。但是,这种对象的有效性只能在其工作期间进行检查,这通常会导致在其工作过程中出现错误。使用 in_found_result 中的 found 布尔值允许开发人员在对迭代器执行操作之前检查元素是否已找到,从而最大限度地减少错误发生。

3. 简化的条件逻辑

在 found 结果中,可以根据搜索结果编写条件逻辑,这非常方便。与其编写迭代器与范围末尾的比较代码,不如使用布尔值“found”来分割逻辑。这不仅减少了代码行数,而且以更“自然”的方式做到这一点,因为代码的目的是也更清晰。

4. 改进的可维护性

尤其是在更大的项目或团队环境中,可维护性方面非常重要。使用 in_found_result 使代码中与搜索相关的部分更具描述性,从而提高了可维护性。未来将处理代码的人员将只知道代码的作用,而无需深入了解迭代器比较的工作细节;因此,他们在更新代码时不会犯几个错误。

使用 in_found_result 的缺点

1. 简单情况下的复杂性增加

尽管 in_found_result 在需要迭代器和成功状态的情况下很有用,但它的使用会给更简单的用例带来不必要的混乱。例如,如果在给定任务中,一个人只需要知道一个元素是否属于某个范围,并且不需要使用迭代器,那么 in_found_result 可能会过度复杂化任务,而不会在效率或有效性方面获得任何收益。在这些情况下,也可以使用更简单的返回类型,如可选的比较或直接的迭代器比较。

2. 性能关键型应用的开销

因此,即使是微小的开销在性能关键型应用中也可能变成严重的问题。由于 in_found_result 绑定到 struct_type 并与迭代器和布尔值一起返回,虽然这种开销通常很小,但在性能关键的特殊情况下,以及每当搜索操作在几行代码的循环中执行时,可以设想 struct 的创建及其成员的访问与简单数据类型相比会产生轻微的惩罚。

3. 用例有限

因此,在需要结果及其位置的搜索操作的情况下,in_found_result 表现出色。在许多情况下,开发人员只需要其中一项信息。例如,如果客户端只关心某个元素是否存在,那么返回一个布尔值就足够了。另一方面,如果您只需要迭代器,那么布尔值可能甚至不存在。在这些情况下,因此,in_found_result 很可能是过度的,并且可能存在更简单的解决方案。

结论

总之,in_found_result 改进了 C++ 代码的可读性以及搜索操作的返回类型,该返回类型由迭代器和布尔字段组成。这降低了出错的可能性,使条件的使用更加直接,并使代码更易于维护。但在其他情况下,当不完全需要时,它可能会增加额外的代码,并且在最坏的情况下,在重要应用中可能对性能产生边际影响。对于大多数用例,尤其是对于细粒度搜索结果有用的情况,in_found_result 是有效的,它体现了现代 C++ 的特性。它还有助于学习使用它的可能性、它的缺点和局限性,这些都有助于生成更紧凑、更易读的代码,并了解它的适用性。