2

这个例子:

#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 来访问非参与值。

为什么标准在这里不需要 throwing 或 sigsegv?

4

3 回答 3

10

为什么标准在这里不需要 throwing 或 sigsegv?

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

通过指定行为未定义,该标准允许实现在每次间接时检查 optional 是否为空。分支执行可能比不分支慢。

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

于 2018-11-07T18:47:38.937 回答
8

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

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

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

现在编译器可以自由地在那里插入检查,有些在调试版本中这样做。

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

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

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

于 2018-11-07T18:52:15.040 回答
6

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

要求:*this 包含一个值。

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

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

所以你对结果没有任何期望。从未定义行为的定义:

本文件没有要求的行为

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

用户可以选择通过has_valuevalue_or自己承担成本。如果用户想要一个可以抛出的操作,他们可以使用value

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

于 2018-11-07T18:48:31.443 回答