对于某些标准库类,访问其部分内容可能会失败。通常,您可以在一些潜在的抛出方法和标记为noexcept
的方法之间进行选择。后者在前提条件下省去了检查,所以如果你想自己承担责任,你可以。这可以在不允许使用异常的情况下使用,或者在修复性能瓶颈时使用。
示例1:std::vector
元素访问:
std::vector<int> vec;
vec.at(n) // throws std::out_of_range
vec[n] // potentially UB, thus your own responsibility
示例2:std::optional
访问:
std::optional<int> optn;
optn.value() // throws std::bad_optional_access
*optn // potentially UB, thus your own responsibility
现在转到std::variant
。直接访问替代方案在某种程度上遵循以下模式:
std::variant<std::string, int> var;
std::get<int>(var) // potentially throwing std::bad_variant_access
*std::get_if<int>(&var) // potentially UB, thus your own responsibility
但这一次签名更改,我们必须注入*
和&
。这样做的缺点是我们无法获得自动移动语义。还有一件事要记住。。。
但如果你看一下std::visit(Visitor&& vis, Variants&&... vars)
,情况会变得更糟。虽然它只抛出,但没有noexcept
可供选择
如果vars中的任何变量是valueless_by_exception。
这意味着对于访问变体,您不能选择自己承担责任,如果您别无选择,必须避免出现异常,则根本无法使用标准工具访问std::variants
!(除了switch
在variant::index()
上的糟糕变通方法之外)
对我来说,这看起来是一个相当糟糕的设计疏忽。。。或者这是有原因的?如果我对监督的看法是正确的,有没有在标准中解决这个问题的举措?
这意味着对于访问变体,您不能选择自己承担责任
当然可以。只有在现有variant
中分配或设置值时,才会出现"异常无值"状态。此外,根据定义,只有在这些过程中实际抛出异常时,才会发生这种情况。这不是一种只发生在随机CCD_ 13上的状态。
如果你有责任确保你永远不会对变体进行模板/赋值,或者你使用的类型在这种情况下永远不会抛出,或者你对任何异常的响应方式是不会与引发它的variant
对话(即:如果抛出bad_alloc
,你的应用程序就不会捕获它;它只是关闭),那么你就不必在意这种可能性了。
基本上,如果已经编码以避免异常,那么visit
的非noexcept
状态是不相关的。除非抛出异常,否则任何variant
都不会进入"异常无值"。