赋值的计算顺序,对于一个基本类型的变量,右操作数抛出异常

Computation sequence of assignment, for a variable of fundamental type, with right operand throwing an exception

如果我使用可能抛出的表达式为基本类型的变量赋值,是否保证抛出异常时变量的状态不变?

例如,在下面的代码中,如果我们选择 'Option A' 并且 operator new() 抛出异常,那么成员是否有 any 机会当在堆栈展开期间调用析构函数时,变量 ptr 将不等于 nullptr

#include <new>  // operator new   operator delete

class MemoryHolder {
  private:
    void * ptr;

  public:
    MemoryHolder () {
      ptr = nullptr;
    }

    void increase_memory () {
      operator delete (ptr);
      ptr = nullptr;

      // Option A (unsafe?)
      ptr = operator new (10);

      // Option B (safe)
      void * new_ptr = operator new (10);
      ptr = new_ptr;
    }

    ~MemoryHolder () {
      operator delete (ptr);
    }
};

我很想知道 C++14 和 C++17 的答案。

我的猜测:'Option A' 在 C++14 和 C++17 中是安全的,因为它们都包含此语句(参见 § [expr.ass] ¶ 1):

In all cases, the assignment is sequenced after the value computation of the right and left operands

但是,我不确定执行左操作数的 'value computation' 是否不包括为其赋予新值。我没有在标准中找到 'value computation' 的定义。

相关(但我不清楚):

in the following code, if we choose 'Option A' and an exception is thrown by operator new(), is there any chance that the member variable ptr will not be equal to nullptr when the destructor will be called during the stack unwinding?

我会暂时忘记 ptrnew 调用之前实际上从未在 MemoryHolder 中初始化,因此可能具有不确定的值。因此,您的问题是 "will ptr have the same value before the assignment as it does after?"

就您可以实际验证这一点的程度而言(在构造函数中,意味着您必须实际捕获构造函数内部的异常),是的,它将具有相同的值。

I did not find a definition of 'value computation' in the standard.

虽然该标准有时会深入研究古怪的语言,但它并不是要主动欺骗您。如果一个术语没有定义,那么它应该按字面意思来理解。 "Value computation" 表示...计算值。您正在将 A 分配给 B。这意味着弄清楚 A 和 B 是什么。其中涉及为它们计算值,然后执行赋值。

Related (but unclear to me): Assignment in C++ occurs despite exception on the right side

这与您的问题无关,因为那是关于作业双方 "value computation" 的顺序,而不是关于 执行 作业。那个问题的OP弄错了。分配从未真正发生过。只是 map::operator[] 将在地图中创建一个条目(如果条目尚不存在)。由于 C++14 没有对赋值操作的两侧进行排序,因此实现可以允许它们以任何顺序发生。在这种情况下,operator[] 先发生,因此即使没有发生赋值也插入了一个元素。