显然,如果使用 C++17 或 boost,std::optional
是从函数返回可选值的最佳选择(另请参阅 GOTW #90)
std::optional<double> possiblyFailingCalculation()
但是,如果一个人坚持使用旧版本(并且无法使用 boost),那么什么是最好的选择?
我看到几个选项:
STL 智能指针(仅限 C++11)
std::unique_ptr<double> possiblyFailingCalculation();
- (+) 与可选用法几乎相同
- (−) 混淆了指向非多态类型或内置类型的智能指针
将其与布尔值配对
std::pair<double,bool> possiblyFailingCalculation();
旧式
bool possiblyFailingCalculation(double& output);
- (−) 与新的 C++11
auto value = calculation()
样式不兼容
- (−) 与新的 C++11
DIY 模板:具有相同功能的基本模板很容易编码,但是实现一个强大的
std::optional<T>
类似模板有什么陷阱吗?引发异常
- (−) 有时"无法计算"是一个有效的返回值。
std::optional
,就像它的boost::optional
父级一样,是一个非常基本的类模板。它是一个bool
,一些存储和一堆方便的成员函数,其中大部分是一行代码和一个断言。
DIY选项绝对是首选。(1) 涉及分配和 (2)、(3) 涉及必须构造一个 T
,即使你想要一个空值 - 这对double
来说根本不重要,但对于更昂贵的类型来说确实很重要。对于 (5),例外不能替代optional
。
您可以随时将您的实现与 Boost 的进行比较。毕竟,这是一个仅包含标头的小型库。
我也会考虑一个哨兵值。
在double
的情况下,NaN
值(std::numeric_limits<double>::quiet_NaN()
)是一个可能的候选值(只有在std::numeric_limits<double>::has_quiet_NaN == true
时才有意义)。
关于这种方法有各种意见(例如,如果更喜欢使用 -ffast-math,请查看 NaN 或 false 作为双精度返回值和双精度的良好哨兵值)。
在特定域中,可能还有其他有意义的哨兵值。
无论如何(不仅对于double
),我会采用/实现类似 markable
(https://github.com/akrzemi1/markable) 的东西来避免魔术值并指示该值可能不存在,并且用户应该检查它的潜在缺失。
有关此方法的其他动机和概述:有效的可选值。
而不是std::optional
,请使用此链接中的tl::optional
:https://github.com/TartanLlama/optional
它具有与std
对应的相同的公共接口,只是它也在 C++98 中编译。
我在生产代码 (C++11) 中使用它并且效果很好!