为什么在访问空std :: optional时没有throw或sigsegv?

vla*_*don 2 c++ segmentation-fault language-lawyer c++17 stdoptional

这个例子:

#include <optional>
#include <iostream>

using namespace std;

int main()
{
    optional<int> t{}; // nullopt (empty) by default

    cout << *t << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

实际上这个程序打印一些int(未初始化的类型值int).此外,libcxx使用assert-check来访问非参与值.

为什么标准不要求扔或sigsegv在这里?

eer*_*ika 10

为什么标准不要求扔或sigsegv在这里?

因为要求某些特定行为隐含地强制要求添加分支以检查是否应该发生该行为 - 无论是抛出还是其他行为.

通过指定行为未定义,标准允许实现检查每个间接时可选是否为空.分支执行可能比不分支更慢.

委员会不是强制要求安全,而是让标准库实施者选择性能(和简单性).您测试的实现似乎选择不抛出异常或以其他方式通知您错误.

  • _"委员会选择了性能而非安全性."_委员会选择将其留给std :: lib实现来决定,并且实现可能会选择某些配置中的性能而其他配置中是安全的(例如,当启用断言时). (5认同)
  • 如果用户可以通过自己的手动测试获得相同的结果,则委员会不会选择强加的开销.这不是让实现选择他们想要的解决方案,实现者可以添加他们想要的任何安全检查,前提是它不违反标准的任何其他部分(如复杂性要求(但即使这样,也有一些用于调试版本)) . (2认同)

Yak*_*ont 8

C++包含未定义行为的概念.

并非所有C++操作都具有标准定义的行为.这允许编译器假设它们永远不会发生,并且在许多情况下可以导致更快的代码.

这里,通过保留std::optional未定义的未使用的结果,访问存储在a std::optional中的数据的成本与访问未存储在a中的数据的成本相同std::optional.唯一的成本是需要额外的空间,而你作为程序员承诺跟踪是否参与.

现在编译器可以在那里插入检查,有些则在调试版本中.

请注意,通常C++ std库类型包括访问数据的安全不安全方法.

无效指针有时会导致sigsev的事实是因为大多数操作系统保护地址大约为0并且崩溃访问它的程序.这是因为这是一个低成本,它从许多汇编,C和C++程序中捕获了一堆不良行为.

如果你想在空时抛出可选项,请使用.value().如果不这样做,请使用operator*.如果您想要一个默认值,如果不存在,请使用.value_or.

  • 不只是在地址0附近.每当你跨越页面边界时,如果页面没有映射,你通常会遇到错误.如果页面位于您的地址空间(操作系统只是将其映射到),则不致命,但在其他方面是致命的. (3认同)

Sha*_*our 6

因为它是未定义的行为,所以[optional.observe] p5部分说:

要求:*这包含一个值.

并且违反了一个require子句是未定义的行为,来自[res.on.required#1] p1,这是在图书馆范围内的要求:

违反函数的Requires:元素中指定的任何前提条件会导致未定义的行为,除非函数的Throws:元素指定在违反前提条件时抛出异常.

所以你没有关于结果的预期.从未定义行为的定义:

本文档没有要求的行为

要求实施检查将是一项成本,并非所有用户都希望获得该成本.因此,这成为一个实施质量问题.实现可以在不同的操作模式下执行检查,例如在启用断言时.

用户可以选择通过has_valuevalue_or自行获取成本.如果用户想要可以抛出的操作,他们可以使用.

请注意,sigsegv,segfaults等...是一个实现定义的行为.