1

If I use an expression that may throw to assign a value to a variable of fundamental type, is it guaranteed that the state of the variable is unaltered if an exception is thrown?

For instance, 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?

#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);
    }
};

I am interested to know the answer for both C++14 and C++17.

My guess: 'Option A' is safe in C++14 and C++17 because they both contain this statement (cf. § [expr.ass] ¶ 1):

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

However, I am not sure that performing the 'value computation' of the left operand doesn't include giving it its new value. I did not find a definition of 'value computation' in the standard.

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

RalphS
  • 442
  • 2
  • 14

1 Answers1

2

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?

I will forget for a moment that ptr is never actually initialized in MemoryHolder before the new call and therefore may have an indeterminate value. Thus, your question is "will ptr have the same value before the assignment as it does after?"

To the degree that you can actually verify this (which in the constructor, means you'd have to actually catch the exception inside of the constructor), yes, it will have the same value.

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

While the standard sometimes delves into oddball language, it is not trying to actively deceive you. If a term is not defined, then it should be taken at face value. "Value computation" means... computing the value. You're assigning A to B. That means figuring out what both A and B are. Which involves computing values for them, then performing assignment.

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

That's unrelated to your question, because that's about the order of the "value computation" for both sides of the assignment, not about performing the assignment. The OP of that question got it wrong. The assignment never actually happens. It's just that map::operator[] will create an entry into the map if one does not already exist. And since C++14 does not sequence the two sides of the assignment operation, implementations can allow them to happen in any order. And in that case, operator[] happened first, thus an element was inserted even though no assignment happened.

Nicol Bolas
  • 378,677
  • 53
  • 635
  • 829
  • There may be a confusion. I assume that the constructor is executed without any problems (no exception is thrown). I should have said that in my question. I will simplify the code in my question to remove any ambiguity. – RalphS Jan 16 '19 at 04:05
  • @RalphS: That doesn't change anything I said, as my statement covers all of the places in your code where you call `new`. – Nicol Bolas Jan 16 '19 at 04:06
  • You are right. Nevertheless, I changed the behaviour of the constructor, in an attempt to improve the value of this Q&A (by making it more straightforward). – RalphS Jan 16 '19 at 04:20