为什么访问空 std::optional 时没有 throw 或 sigsegv?
Why there is no throw or sigsegv while accessing empty std::optional?
例子:
#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?
Why the Standard does not require throwing or sigsegv here?
因为要求某些特定行为隐含地要求添加一个分支来检查该行为 - 无论是抛出还是其他 - 是否应该发生。
通过指定行为未定义,该标准允许实现 not 在每次间接访问时检查可选是否为空。分支执行可能比不分支慢。
委员会没有强制要求安全,而是让标准库实施者选择性能(和简单性)。您测试的实现似乎已选择不抛出异常或以其他方式通知您错误。
因为它是未定义的行为,[optional.observe]p5 部分说:
Requires: *this contains a value.
并且违反 requires 子句是未定义的行为,来自 [res.on.required#1]p1 which is under Library-wide requirements:
Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when the precondition is violated.
所以你对结果没有期望。来自undefined behavior的定义:
behavior for which this document imposes no requirements
要求执行检查会产生成本,并非所有用户都愿意承担该成本。因此,这成为实施质量问题。一个实现可以自由地在不同的操作模式下执行检查,例如在启用断言时。
用户可以选择通过 has_value or value_or. If the user wants an operation that can throw they can use value 自行承担费用。
注意 sigsegv, segfaults etc... are an implementation defined behavior.
C++ 包含未定义行为的思想。
并非所有 C++ 操作都具有标准定义的行为。这允许编译器假设它们永远不会发生,并且在许多情况下可以产生更快的代码。
此处,通过保留未定义未使用的 std::optional
的结果,访问存储在 std::optional
中的数据的成本与访问未存储在 std::optional
中的数据的成本相同一个std::optional
。唯一的成本是需要额外的空间,而作为程序员的你承诺会跟踪它是否被占用。
现在编译器可以自由插入检查,有些在调试版本中也可以。
请注意,通常 C++ std
库类型包括 safe 和 unsafe 方法来访问数据。
无效指针有时会导致 sigsev 的事实是因为大多数 OS 保护 0 附近的地址并使访问它的程序崩溃。这是因为它的成本很低,而且它从许多汇编程序、C 和 C++ 程序中捕获了一堆不良行为。
如果你想可选的在空时抛出,使用.value()
。如果不这样做,请使用 operator*
。如果您想要默认值(如果不存在),请使用 .value_or
.
例子:
#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?
Why the Standard does not require throwing or sigsegv here?
因为要求某些特定行为隐含地要求添加一个分支来检查该行为 - 无论是抛出还是其他 - 是否应该发生。
通过指定行为未定义,该标准允许实现 not 在每次间接访问时检查可选是否为空。分支执行可能比不分支慢。
委员会没有强制要求安全,而是让标准库实施者选择性能(和简单性)。您测试的实现似乎已选择不抛出异常或以其他方式通知您错误。
因为它是未定义的行为,[optional.observe]p5 部分说:
Requires: *this contains a value.
并且违反 requires 子句是未定义的行为,来自 [res.on.required#1]p1 which is under Library-wide requirements:
Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when the precondition is violated.
所以你对结果没有期望。来自undefined behavior的定义:
behavior for which this document imposes no requirements
要求执行检查会产生成本,并非所有用户都愿意承担该成本。因此,这成为实施质量问题。一个实现可以自由地在不同的操作模式下执行检查,例如在启用断言时。
用户可以选择通过 has_value or value_or. If the user wants an operation that can throw they can use value 自行承担费用。
注意 sigsegv, segfaults etc... are an implementation defined behavior.
C++ 包含未定义行为的思想。
并非所有 C++ 操作都具有标准定义的行为。这允许编译器假设它们永远不会发生,并且在许多情况下可以产生更快的代码。
此处,通过保留未定义未使用的 std::optional
的结果,访问存储在 std::optional
中的数据的成本与访问未存储在 std::optional
中的数据的成本相同一个std::optional
。唯一的成本是需要额外的空间,而作为程序员的你承诺会跟踪它是否被占用。
现在编译器可以自由插入检查,有些在调试版本中也可以。
请注意,通常 C++ std
库类型包括 safe 和 unsafe 方法来访问数据。
无效指针有时会导致 sigsev 的事实是因为大多数 OS 保护 0 附近的地址并使访问它的程序崩溃。这是因为它的成本很低,而且它从许多汇编程序、C 和 C++ 程序中捕获了一堆不良行为。
如果你想可选的在空时抛出,使用.value()
。如果不这样做,请使用 operator*
。如果您想要默认值(如果不存在),请使用 .value_or
.