C++ std::bad_可选_访问是针对异常的小型犯罪吗?

C++ std::bad_可选_访问是针对异常的小型犯罪吗?,c++,exception,std,optional,hierarchy,C++,Exception,Std,Optional,Hierarchy,如果在optional没有初始化实际值时调用std::optional的value()成员函数,则抛出std::bad\u optional\u访问。由于它直接从std::exception派生,因此需要catch(std::bad_可选的\u访问常量&)或catch(std::exception const&)来处理异常。然而,这两种选择对我来说似乎都是悲哀的: std::exception捕获每个异常 std::bad_可选_访问公开实现细节。考虑下面的例子: 因此,要捕获异常,您需要充

如果在
optional
没有初始化实际值时调用
std::optional
value()
成员函数,则抛出
std::bad\u optional\u访问。由于它直接从
std::exception
派生,因此需要
catch(std::bad_可选的\u访问常量&)
catch(std::exception const&)
来处理异常。然而,这两种选择对我来说似乎都是悲哀的:

  • std::exception
    捕获每个异常
  • std::bad_可选_访问
    公开实现细节。考虑下面的例子:
因此,要捕获异常,您需要充分了解
的实现中
std::optional
的用法,并将其引导到已知问题的列表中。我不想捕获并重写
std::bad_optional_access
,因为(对我来说)异常的关键部分是在需要时忽略它们的可能性。这就是我对正确方法的看法:

std::exception
  <- std::logic_error
    <- std::wrong_state (doesn't really exist)
      <- std::bad_optional_access (isn't really here)
最后,

  • 为什么
    std::bad_可选_访问
    设计成这样
  • 我感觉异常正确吗?我的意思是,它们是为这种用途而引入的吗
注意:
boost::bad_可选_访问
源自
std::logic_error
。很好

注2:我知道
catch(…)
和抛出类型不同于
std::exception
家族的对象。为了简洁(和理智),省略了它们


更新:不幸的是,我不能接受两个答案,所以:如果你对这个话题感兴趣,你可以阅读胡祖甸的答案和他们的评论

首先,如果您不想公开该实现,那么异常甚至不应该跨越实现和客户机代码之间的边界。这是一个常见的习惯用法,任何异常都不应跨越库、API等的边界

接下来,将某些内容存储在
可选的
中是您应该自己控制的实现。这意味着您应该检查可选项是否为空(至少如果您不希望客户端知道实现的详细信息)


最后,回答以下问题:客户机代码对空对象执行操作是错误吗?如果允许这样做,则不应抛出任何异常(例如,可能返回错误代码)。如果这是一个不应该发生的实际问题,那么抛出异常是合适的。您可以捕获代码中的
std::bad_optional_access
,并从
catch
块抛出其他内容。

如果您不想公开该实现,那么异常甚至不应该跨越实现和客户机代码之间的边界。这是一个常见的习惯用法,任何异常都不应跨越库、API等的边界

接下来,将某些内容存储在
可选的
中是您应该自己控制的实现。这意味着您应该检查可选项是否为空(至少如果您不希望客户端知道实现的详细信息)

最后,回答以下问题:客户机代码对空对象执行操作是错误吗?如果允许这样做,则不应抛出任何异常(例如,可能返回错误代码)。如果这是一个不应该发生的实际问题,那么抛出异常是合适的。您可以捕获代码中的
std::bad_optional_access
,并从
catch
块抛出其他内容

因此,要捕获异常,您需要充分了解
std::optional
在项目实现中的用法

不,要捕获异常,您必须阅读
get\u placement
的文档,该文档将告诉您它抛出
std::bad\u可选访问权。通过选择发出该异常,函数将该异常的发出作为该函数接口的一部分

因此,与直接返回一个
std::optional
时相比,它不再依赖于
项的实现。您选择将其放在界面中,因此您应该承担后果

换句话说,如果您觉得将
std::optional
作为参数类型或返回值是错误的,那么直接发出
bad\u optional\u异常
也应该有同样的感觉


归根结底,这一切都可以追溯到错误处理的一个最基本问题:在错误的具体性质变得毫无意义甚至完全不同之前,你能离错误发生地有多远

假设您正在进行文本处理。有一个文件,每行包含3个浮点数。您正在逐行处理它,并将每组三个值插入到一个列表中。还有一个函数将字符串转换为浮点数,如果转换失败,它将抛出一个异常

因此,代码大致如下所示:

try {
  unit.equip_item(item);
} catch (std::wrong_state const& exception) { // no implementation details
  inform_client(exception.what());
}
for each line
  split the line into a 3-element list of number strings.
  for each number string
    convert the string into a number.
    add the number to the current element.
  push the element into the list.
好吧,那么。。。如果字符串到浮点转换器抛出,会发生什么情况?视情况而定;你想发生什么?这取决于谁抓住了它。如果需要错误的默认值,则最内层循环中的代码将捕获该错误并将默认值写入元素

但您可能希望记录某一行有错误,然后跳过该行(不要将其添加到列表中),但继续正常处理其余文本。在这种情况下,您将在第一个循环中捕获异常

此时,错误的含义已经改变。抛出的错误是“此字符串不包含有效的浮点”,但代码不是这样处理的。事实上,捕获代码完全丢失了错误的上下文。它不知道是什么时候
try {
  unit.equip_item(item);
} catch (std::wrong_state const& exception) { // no implementation details
  inform_client(exception.what());
}
for each line
  split the line into a 3-element list of number strings.
  for each number string
    convert the string into a number.
    add the number to the current element.
  push the element into the list.