可选项是可为空的值类型。
Ashared_ptr
是可以为空的引用计数引用类型。
Aunique_ptr
是可以为空的仅移动引用类型。
它们的共同点是它们可以为空——它们可以“不存在”。
它们是不同的,其中两个是引用类型,另一个是值类型。
值类型有几个优点。首先,它不需要在堆上分配——它可以与其他数据一起存储。这消除了可能的异常源(内存分配失败),可以更快(堆比堆栈慢),并且对缓存更友好(因为堆往往相对随机排列)。
引用类型还有其他优点。移动引用类型不需要移动源数据。
对于非仅移动引用类型,您可以使用不同名称对相同数据进行多个引用。具有不同名称的两种不同值类型总是引用不同的数据。无论哪种方式,这都可能是优势或劣势;但它确实使对值类型的推理变得更加容易。
推理shared_ptr
是非常困难的。除非对数据的使用方式进行一套非常严格的控制,否则几乎不可能知道数据的生命周期。推理unique_ptr
要容易得多,因为您只需要跟踪它的移动位置。关于 ' 生命周期的推理optional
是微不足道的(嗯,就像你嵌入它一样微不足道)。
可选接口增加了一些类似单子的方法(如.value_or
),但这些方法通常可以很容易地添加到任何可为空的类型中。不过,目前,它们是为了optional
而不是为了shared_ptr
or unique_ptr
。
optional 的另一个大好处是,您非常清楚您有时希望它可以为空。C++ 中有一个坏习惯是假定指针和智能指针不为空,因为它们的使用不是可空的。
因此代码假定某些共享或唯一的 ptr 永远不会为空。它通常有效。
相比之下,如果你有一个可选的,你拥有它的唯一原因是因为它有可能实际上是空的。
在实践中,我对将 aunique_ptr<enum_flags> = nullptr
作为参数持怀疑态度,我想说“这些标志是可选的”,因为在调用者身上强制进行堆分配似乎很粗鲁。但是 anoptional<enum_flags>
不会强制调用者这样做。非常便宜的optional
特性让我愿意在很多情况下使用它,如果我唯一的可空类型是智能指针,我会找到一些其他的解决方法。
这消除了“标志值”的大部分诱惑,例如int rows=-1;
. optional<int> rows;
具有更清晰的含义,并且在调试中会告诉我何时使用行而不检查“空”状态。
可以合理失败或不返回任何感兴趣的函数可以避免标志值或堆分配,并返回optional<R>
。例如,假设我有一个可放弃的线程池(例如,当用户关闭应用程序时停止处理的线程池)。
我可以std::future<R>
从“队列任务”函数返回并使用异常来指示线程池已被放弃。但这意味着线程池的所有使用都必须针对“来自”异常代码流进行审核。
相反,我可以 return std::future<optional<R>>
,并向用户提示他们必须在他们的逻辑中处理“如果该过程从未发生过会发生什么”。
“来自”异常仍然可能发生,但它们现在是异常的,不是标准关闭程序的一部分。
在其中一些情况下,expected<T,E>
一旦它在标准中,将是一个更好的解决方案。