【问题标题】:Why use boost::optional when I can return a pointer [closed]为什么在我可以返回指针时使用 boost::optional [关闭]
【发布时间】:2016-02-09 15:59:58
【问题描述】:

如果我有一个 find 函数有时无法找到所需的东西,我倾向于让该函数返回一个指针,以便 nullptr 表示找不到该东西。

例如

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

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

我看到boost::optional 也为此目的而提倡。例如。 When to use boost::optional and when to use std::unique_ptr in cases when you want to implement a function that can return "nothing"?

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

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

【问题讨论】:

  • find 函数返回值类型的情况如何,例如intdouble?
  • 另外,从您的链接问题中,stackoverflow.com/a/14360932/2920343
  • @CompuChip 这有意义吗?你只是按价值返回双倍对吗?如果您希望用户能够编辑 SomeClass 拥有的内存中的特定 double,那么您将再次返回 double*。
  • @nappyfalcon:整个问题都是关于可选的。如果找不到值怎么办?
  • @nappyfalcon:optional 通过将结果按值复制到调用者(或可能在 C++11 中移动它)来解决此问题。所有权现在以临时的形式由调用者掌握,然后以具有清晰语义的方式销毁

标签: c++ c++11 boost-optional


【解决方案1】:

这里optional<Student&> 返回类型的优势在于,所有熟悉optional 的用户都可以很容易地了解用法的语义(一旦他们熟悉了它就会变得很明显)。这些语义是:

  • 调用者不拥有Student,也不负责内存管理。调用者只需获取对现有对象的引用。
  • 很明显,此功能可能会失败。您可能获得价值,而您可能一无所获。很明显,调用者需要以一种或另一种方式检查结果。

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

【讨论】:

  • optional<T&> 在该类型的当前 TS 版本中是明确非法的。 optional 用于值类型。
  • 嗯,那需要optional<reference_wrapper<T>>。丑陋。
  • @Angew make_optional(std::ref(x))
  • @bolov 我的意思是在指定返回类型时。让我们假设auto 不是一个选项。你可以用decltype 做一些魔术,但它也很丑。
  • @Barry:因为我们已经有了;它被称为“指针”。 optional 用于值类型。
【解决方案2】:

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

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


现在,optional<T> 是另一种野兽。

我在基于容器的查找算法中使用了optional<Iterator>。我没有返回end(),而是返回空的可选。这让用户无需比较即可确定他们是否未能找到该项目,并让代码如下:

if(linear_search_for( vec, item))

工作,而相同的算法还可以让您同时获得项目如果您确实需要项目在容器中的位置。

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

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

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

Rect GetRect();

现在,这很棒。但是如果这个问题没有意义呢?嗯,一种方法是返回一个空矩形或其他“标志”值。

Optional 让您传达它可以返回一个矩形,或者什么都不返回,并且不使用空矩形来表示“无”状态。它使返回值可以为空。

int GetValue();

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

相反,optional<int> GetValue() 明确表示它可能会失败,并且它的失败状态是什么。如果它被填充,你就知道它是一个真实的值,而不是一个标志值。

在这两种情况下,返回非拥有指针是不可行的,因为谁拥有存储空间?返回一个拥有的指针是昂贵的,因为无意义的堆分配是没有意义的。

Optional 是可以为空的值类型。当您想在本地管理资源,并且仍然想要一个空状态时,它们会明确表示。


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

【讨论】:

  • 我喜欢“因为毫无意义的堆分配是毫无意义的”——“因为愚蠢是愚蠢的”。说得好,先生!
【解决方案3】:

optional<T&> 确实可以替换为T*,但T* 的语义不明确(所有权?)。

但是optional<T>不能被T*代替。 例如:

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

如果没有重叠,T* (nullptr) 或optional&lt;T&gt; 没有问题。 但是如果有重叠,我们需要创建一个新的区间。在这种情况下,我们可能会返回一个 smart_pointer,或者是可选的。

【讨论】:

    【解决方案4】:

    假设您有一个std::map&lt;IndexType, ValueType&gt;,您正试图在其中找到一些东西(注意:这同样适用于其他容器,这只是一个示例)。您有以下选择:

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

    【讨论】:

    • 前2种情况:可以返回const引用或者指针,让用户无法修改
    猜你喜欢
    • 1970-01-01
    • 2012-10-30
    • 2010-09-14
    • 1970-01-01
    • 2022-01-02
    • 1970-01-01
    相关资源
    最近更新 更多