当我可以返回指针时,为什么要使用 boost::可选

Why use boost::optional when I can return a pointer

本文关键字:boost 可选 为什么 返回 我可以 指针      更新时间:2023-10-16

如果我有一个有时找不到所需内容的find函数,我倾向于让该函数返回一个指针,以便nullptr指示未找到该事物。

例如

Student* SomeClass::findStudent(/** some criteria. */)

如果 Student 存在,它将返回指向找到的 Student 对象的指针,否则它将返回 nullptr

我也看到boost::optional提倡这样做。 例如,当您想要实现可以返回"nothing"的函数时,何时使用 boost::optional,何时使用 std::unique_ptr?

我的问题是,在这种情况下,返回指针不是最好的解决方案,即有可能找不到查询的项目,在这种情况下,返回 nullptr 是一个完美的解决方案。使用类似boost::optional(或任何其他类似解决方案(的优势是什么?

请注意,在我的示例中,findStudent只会返回指向 SomeClass 拥有的对象的指针。

此处optional<Student&>返回类型的优点是,用法的语义对于所有熟悉optional的用户来说都是显而易见的(一旦他们熟悉了它,就会变得显而易见(。这些语义是:

  • 调用方不拥有Student,也不负责内存管理。调用方只是获取对现有对象的引用。
  • 很明显,此功能可能会失败。你也许得到一个价值,但你可能什么也得不到。很明显,调用方需要以一种或另一种方式检查结果。

optional<T>以一种T*不是的方式自我记录。此外,它还有其他好处,因为它可以在您想要返回任何类型的对象类型而无需分配的情况下工作。如果您需要退回intdoubleSomePOD怎么办?

optional<T&>被从C++标准化轨道中删除,因为它的使用是有问题的:它的行为与非拥有T*几乎相同,语义略有不同(并且与optional<T>T*略有不同(。

optional<T&>基本上是一个非拥有T*,包装得很漂亮,而且有点奇怪。


现在,optional<T>是一头不同的野兽。

我已经在基于容器的查找算法中使用了optional<Iterator>。 我不是返回end(),而是返回空的可选。 这使用户可以在没有比较的情况下确定他们是否未能找到该项目,并允许编写如下代码:

if(linear_search_for( vec, item))

工作,而相同的算法还可以让您在实际需要时同时获取物品物品在容器中的位置。

指向元素的指针不会提供您可能想要的位置信息,除非使用连续容器。

所以在这里,我创建了一个可为空的迭代器,它具有迭代器(通常使用不同类型的容器(和指针(可以测试空状态(的优点。

下一个用途实际上是返回一个值。 假设您有一个计算矩形的函数。

Rect GetRect();

现在,这很棒。 但是,如果这个问题可能毫无意义呢? 好吧,一种方法是返回一个空的矩形或其他"标志"值。

可选允许您传达它可以返回一个矩形或不返回任何内容,而不是将空矩形用于"无"状态。 它使返回值可为空。

int GetValue();

是一个更好的例子。 无效值可以使用 int 的标志状态 - 比如-1 - 但这会强制函数的每个用户查找和跟踪标志状态,而不是意外地将其视为正常状态。

相反,optional<int> GetValue()清楚地表明它可能会失败,以及失败说明它的内容。 如果它已填充,则知道它是一个实际值,而不是标志值。

在这两种情况下,返回非拥有指针都是不可行的,因为谁拥有存储? 返回拥有指针的成本很高,因为毫无意义的堆分配毫无意义。

可选值类型为空值类型。 当您想要在本地管理资源,并且仍然想要空状态时,他们会明确表示。


要研究的另一件事是提出的expected类型。 这是一个可选选项,但当处于空状态时,包含其为空的原因

optional<T&>确实可以替换为T*T*没有明确的语义(所有权?

optional<T>不能被T*取代.例如:

optional<Interval> ComputeOverlap(const Interval&, const Interval&);

如果没有重叠,则T*(nullptr(或optional<T>没有问题。但如果存在重叠,我们需要创建一个新的间隔。在这种情况下,我们可能会返回smart_pointer,或者可选。

让我们假设您有一个尝试查找某些东西的std::map<IndexType, ValueType>(注意:这同样适用于其他容器,这只是为了举个例子(。您有以下选项:

  • 你返回一个ValueType&:用户可以修改你的映射内容,不需要考虑内存分配/释放。但是,如果您在地图中找不到任何内容,则需要抛出异常或类似内容。
  • 您返回一个ValueType*:用户可以修改您的地图内容,如果您没有找到任何内容,您可以返回 nullptr。但是用户可以在该指针上调用 delete,并且您必须指定他是否必须这样做。
  • 您返回一个智能指针指向ValueType:用户不必担心删除或不删除,并且可以根据智能指针的类型修改您的地图内容。您也可以返回 nullptr。但这几乎需要您处理地图中的smart_pointers,如果ValueType例如只是一个整数,那就太复杂了。
  • 你返回一个简单的ValueType:用户不能修改你的映射内容,也不需要考虑内存分配/释放。但是,如果您在地图中找不到任何内容,则需要返回一些特殊ValueType,告诉用户您没有找到任何东西。如果您的ValueType例如 int,你会返回哪一个明确表示"找不到int"。
  • 您返回一个 boost::optional,这是您可以获得的最接近的简单ValueType按值返回,并带有"不返回ValueType"的附加选项