【问题标题】:Best alternative to std::optional to return an optional value from a method? (using C++98/C++11/C++14)std::optional 从方法返回可选值的最佳替代方案? (使用 C++98/C++11/C++14)
【发布时间】:2016-07-02 04:40:27
【问题描述】:

显然,如果使用 C++17 或 boost,std::optional 是从函数返回可选值的最佳选择(另请参阅 GOTW #90

std::optional<double> possiblyFailingCalculation()

但是,如果一个人被旧版本卡住(并且不能使用 boost),那么最好的选择是什么?为什么?

我看到了几个选项:

  1. STL 智能指针(仅限 C++11)

    std::unique_ptr<double> possiblyFailingCalculation();
    
    • (+) 与可选的用法几乎相同
    • (-) 对非多态类型或内置类型的智能指针感到困惑
  2. 与布尔配对

    std::pair<double,bool> possiblyFailingCalculation();
    
  3. 旧式

    bool possiblyFailingCalculation(double& output);
    
    • (-) 与新的 C++11 auto value = calculation() 样式不兼容
  4. DIY 模板:具有相同功能的基本模板很容易编写代码,但是在实现健壮的std::optional&lt;T&gt; 类似模板时是否存在任何缺陷?

  5. 抛出异常

    • (-) 有时“无法计算”是一个有效的返回值。

【问题讨论】:

  • 还有另一个选项 - 抛出异常,因为你的函数失败了。
  • 我添加了该选项,但有时人们想要无异常的函数,并且“无法计算”只是一个常规的返回值。例如。你不想要函数 Edge getLargestEdge();在 Triangle 类中,在等边三角形的情况下抛出异常。
  • @DavidHaim:将其发布为答案;除非失败是一种非常常见的情况,否则人们会希望任何开销异常处理都会相当无意义,避免调用者不检查失败并得到不正确结果的风险是一个值得称赞的目标。
  • @ShadowRanger:如果意见问题是本网站的主题,并且 David 回答“抛出异常”作为返回 std::optional 的最佳选择,我肯定会否决,因为它完全改变了语义。
  • @mr_T 一个更好的例子可能是std::map 查找如何在概念上返回optional&lt;pair&lt;const K, V&gt;&gt;,在查找失败时返回none 而不是end() - 这是一个合理的非异常结果.

标签: c++ c++11 return-value optional


【解决方案1】:

而不是std::optional,使用此链接中的tl::optionalhttps://github.com/TartanLlama/optional

它与 std 对应的公共接口相同,只是它也可以在 C++98 中编译。

我在生产代码 (C++11) 中使用它并且效果很好!

【讨论】:

  • 好建议。来自同一个人的 tl::expected 也非常好。
【解决方案2】:

我也会考虑sentinel value

double 的情况下,NaN 值 (std::numeric_limits&lt;double&gt;::quiet_NaN()) 是可能的候选对象(仅在 std::numeric_limits&lt;double&gt;::has_quiet_NaN == true 时才有意义)。

关于这种方法有各种不同的意见(例如,看看NaN or false as double precision return valueGood sentinel value for double if prefer to use -ffast-math)。

在特定域中可能存在其他有意义的标记值。

在任何情况下(不仅适用于double),我都会采用/实现类似markable (https://github.com/akrzemi1/markable) 来避免魔法值,并表明该值可能不存在,并且它的潜在缺失应该是由用户检查。

有关此方法的其他动机和概述:Efficient optional values

【讨论】:

    【解决方案3】:

    std::optional 和它的boost::optional 父级一样,是一个非常基本的类模板。它是一个bool、一些存储空间和一堆便利的成员函数,其中大部分是一行代码和一个断言。

    DIY 选项绝对是首选。 (1) 涉及分配,(2)、(3) 涉及必须构造一个 T,即使您想要一个空值 - 这对于 double 一点也不重要,但对于更昂贵的类型却很重要。对于 (5),异常不能替代 optional

    您始终可以将您的实现与 Boost 的实现进行比较。毕竟,这是一个小型的仅标头库。

    【讨论】:

    • 它来自 c++17,在 c++11 上不可用
    猜你喜欢
    • 1970-01-01
    • 2013-08-29
    • 2011-11-10
    • 1970-01-01
    • 2019-02-18
    • 2020-09-03
    • 2012-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多