std::optional 相对于 nullptr 的优点/缺点是啥?

Posted

技术标签:

【中文标题】std::optional 相对于 nullptr 的优点/缺点是啥?【英文标题】:What are the advantages/disadvantages of std::optional over nullptr?std::optional 相对于 nullptr 的优点/缺点是什么? 【发布时间】:2021-12-18 15:26:21 【问题描述】:

我在互联网上看了几部在线 std::optional 纪录片。但是,我无法找到以下两种情况之间的任何直接比较:

案例 1:

SomePointer* foo::get_some_pointer(cont int value) 
    
    auto result = myMap.find(value);

    if (result != myMap.end()) 
        return const_cast<SomePointer*>(&result->second);
    

    return nullptr;

案例2

 std::optional<SomePointer*> foo::get_some_pointer (cont int value) 
    
        auto result = myMap.find(value);
    
        if (result != myMap.end()) 
            return std::optional<SomePointer*>&result->second;
        
    
        return std::nullopt;
    

案例 1 相对于案例 2 的优点/缺点是什么(nullopt 优于 nullptr)?

【问题讨论】:

所以,为了返回一个 ponter,实际上不需要将它包装到 std::optional 因为你有nullptr。但是,如果您返回一些没有“空”状态的对象或值,std::optional 会非常有用。 案例2有更多的空值,它同时支持nullptrstd::nullopt,更多的选择:)。更常见的是使用std::optional&lt;SomePointer&gt;,而不使用* 顺便说一句,const_cast非常粗略。 带有std::optional&lt;SomePointer*&gt;,它有三种状态:有一个有效的指针,有一个nullptr,有一个std::nullopt。您的代码小心地返回有效指针或std::nullopt ...但调用者或此后的代码仍应警惕“永远不会发生”nullptr 的可能性。 (嗯,可能有一个悬空指针,或者一个野指针,或者未初始化的指针......我忽略了这些场景。) 为了您的考虑,返回std::optional&lt;std::reference_wrapper&lt;SomePointer&gt;&gt;,它完全避免了nullptr 状态,并且仍然具有打包行为,并且是自记录代码。 (SomePointer 的名字可能很糟糕,因为我认为它不是指针。) 【参考方案1】:

std::optional 的唯一工作是通过额外的“null”值扩展类型域。每个指针类型 T* 已经有一个被视为“null”的值 - nulltpr

因此,直接比较这两者并不是一个好主意,因为它们回答的问题不同。有时区分“无结果”和“空结果”1(这是一种可能的解释)很重要,有时则不然。您应该使用适合您需要的任何一种。

现在如果代码返回指针的唯一原因是利用隐式指针可空性,那么正确的解决方案是将其更改为返回std::optional&lt;SomePointer&gt;(或者可能是std::optional&lt;std::reference_wrapper&lt;SomePointer&gt;&gt; ) 代替,但这不是你问的。


1 当然在这一点上考虑类似的东西也是值得的。 struct NoResult ; using Result = std::variant&lt;NoResult, SomePointer*&gt;; 使其更加明确。

【讨论】:

@MericOzcan 在当前代码中,您在第二个示例中调用at,这是在进行不必要的查找。除此之外,optional&lt;T*&gt;T* 之上的开销应该可以忽略不计(如果有的话)。在这两种情况下都没有指向的值的副本。 @MericOzcan 首先,您需要考虑(强制)返回值优化,这意味着返回值直接在应分配的位置创建。这消除了按价值返回的复制成本。除了包含值的std::optional 和原始指针(或者可能是std::unique_ptr!)之间,应用与非可选对象之间相同的参数,按值或按指针... @MericOzcan 这完全取决于您的具体要求!如果someObject 是一个局部变量,return &amp;someObject; 是非法的(你以这种方式返回一个 dangling 指针!)。按值返回允许在堆栈上分配对象(好吧,假设std::optional 是使用placement-new 和显式析构函数调用实现的,不确定是否由标准强制),出于性能原因,有时可能更可取。返回原始指针可能意味着必须显式返回 delete 对象(在这种情况下,std::unique_ptr 通常是首选)。 如果您需要返回已经存在的对象,这些对象的生命周期由另一个组件控制,原始指针是不错的选择,可以避免任何不必要甚至错误的副本。 @MericOzcan 好吧,那么考虑以下区别:如果您不希望能够返回空/无结果值——您何时按值返回,何时按引用返回?通过值意味着您获得对象的副本,通过引用您仍然引用原始对象。同样适用于std::optional 与原始指针,只是现在您根本无法返回任何对象...

以上是关于std::optional 相对于 nullptr 的优点/缺点是啥?的主要内容,如果未能解决你的问题,请参考以下文章

nullptr、 和 nullopt 的区别

std::optional

将一个 std::optional 转换为另一个 std::optional

std::optional - 用 或 std::nullopt 构造空?

什么时候适合使用 std::optional

如何在 C++ 中使用 std::optional ?