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

时间:2018-11-07 18:40:22

标签: c++ language-lawyer c++17 sigsegv stdoptional

示例:

#include <optional>
#include <iostream>

using namespace std;

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

    cout << *t << endl;

    return 0;
}

实际上,该程序输出一些int(类型为int的未初始化值)。 另外,libcxx使用assert-check来访问未使用的值。

为什么标准不需要在此处抛出或sigsegv?

3 个答案:

答案 0 :(得分:10)

  

为什么标准不需要在此处抛出或sigsegv?

由于需要某种特定的行为,因此暗含了添加一个分支来检查该行为(无论是抛出行为还是其他行为)的要求。

通过指定行为是未定义的,该标准允许实现 not 检查每个间接引用中的optional是否为空。分支执行可能比不分支慢。

委员会没有强制安全性,而是让标准库实施者选择性能(和简便性)。您测试的实现似乎选择了不引发异常或以其他方式通知您错误。

答案 1 :(得分:8)

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

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

在这里,通过保留未定义的std::optional的使用结果,访问std::optional中存储的数据的开销与访问{}中没有存储的数据的开销相同{1}}。唯一的成本就是所需的额外空间,而作为程序员的您将保证跟踪是否参与其中。

现在,编译器可以自由地在其中插入检查,有些可以在调试版本中进行。

请注意,通常C ++ std::optional库类型包括用于访问数据的 safe unsafe 方法。

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

如果要让可选内容在空时抛出,请使用std。否则,请使用.value()。如果您不希望使用默认值,请使用operator*

答案 2 :(得分:6)

由于这是未定义的行为,因此第[optional.observe]p5节说:

  

要求:*其中包含一个值。

违反require子句是未定义的行为,来自[res.on.required#1]p1下的 Library-wide requirements

  

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

因此您对结果没有期望。根据{{​​3}}的定义:

  

本文档不要求其行为的行为

要求执行检查是一项费用,并非所有用户都希望承担该费用。因此,这成为实施质量的问题。实现可以自由地在不同的操作模式下执行检查,例如,在启用断言时。

用户可以选择通过undefined behaviorhas_value自行承担费用。如果用户希望进行可以抛出的操作,则可以使用value_or

请注意,value