代码之家  ›  专栏  ›  技术社区  ›  Adel M.

如果我不立即访问返回的值,为什么在空std::optional上调用运算符*是未定义的行为?

  •  0
  • Adel M.  · 技术社区  · 2 年前

    为什么调用是未定义的行为 operator* 在一个空的 std::optional 如果在放置对象之前不访问返回的值?

    例如,为什么下面的代码是UB?

    std::optional<int> opt_val;
    auto p_val = &(*opt_val);
    opt_val.emplace(0);
        
    // do something with p_val
    

    编辑:最初的问题是问是否是UB,现在问为什么是UB。

    2 回复  |  直到 2 年前
        1
  •  5
  •   463035818_is_not_an_ai    2 年前

    因为标准是这么说的。不是明确的,但 22.5.3.6#1 说关于 operator* :

    前提条件: *this 包含一个值。

    该标准没有定义在不满足前提条件的情况下调用函数时会发生什么。它是未定义的。

    事实上,这已经可以得出答案了。该标准并没有试图定义所有的东西。相反,它指定了一个方法在用于它应该用于的目的时要做什么。使命感 std::optional::operator* 不使用结果就没有用。该方法甚至可以标记为 [[nodiscard]] 以使其在忽略返回的值时出错。该标准没有定义的是未定义的。


    std::optional 不使用动态分配。它为对象建模,而不是为指针建模。尽管如此,呼叫 操作人员 在不包含对象的可选项上,类似于取消引用空指针。

    我想可以编写一个调用没有问题的实现 操作人员 在空的可选项上。尽管如此,标准规定它是未定义的,因此实现不必处理它。这给了实现更多的自由,并可能为优化器提供更多的机会。

        2
  •  4
  •   Swift - Friday Pie    2 年前

    定义一个表达式的特征就是它的结果,由它的值类型和值组织法来描述。语言要求任何表达式都应具有有效的结果(包括 void ),无论是否使用。

    *opt_val 是表达式,必须是类型的左值 int 虽然 opt_val 是的某些glvalue std::optional<int> 明显地前者必须从某个地方出现,这在空的情况下是不可能的 optional (它真的是空的,没有创建任何对象)。

    这就是的全部意义 optional<T> ,它允许有一个透明的工具来引用T的实例,并在需要时为T的实例分配存储的行为。换句话说,您可能会认为它是一个类型安全的包装器 void* ,除了而不是 nullptr 它可以使用也可以不使用不同的null对象。它没有定义如何 std::optional 实现了,因此调用其的效果 operator* 也没有定义。

        3
  •  2
  •   HolyBlackCat    2 年前

    大多数C++标准库实现都有可以启用的可选断言。如果启用这些, operator* 当对空的可选项调用时,将触发断言。

    如果您更改标准以删除此UB,则拥有此断言将成为非法行为。