Title
Deallocation on exception in new before arguments evaluated
Status
cd3
Section
7.6.2.8 [expr.new]
Submitter
Andrei Iltchenko

Created on 2001-06-26.00:00:00 last changed 33 months ago

Messages

Date: 2012-02-15.00:00:00

[Voted into the WP at the February, 2012 meeting; moved to DR at the October, 2012 meeting.]

Date: 2008-03-15.00:00:00

Proposed resolution (March, 2008):

Change 7.6.2.8 [expr.new] paragraph 18 as follows:

If any part of the object initialization described above [Footnote: ...] terminates by throwing an exception, storage has been obtained for the object, and a suitable deallocation function can be found, the deallocation function is called...
Date: 2022-02-18.07:47:23

Suggested resolution:

Change the first sentence of 7.6.2.8 [expr.new] paragraph 17 to read:

If the memory for the object being created has already been successfully allocated and any part of the object initialization described above...
Date: 2022-02-18.07:47:23

According to the C++ Standard section 7.6.2.8 [expr.new] paragraph 21 it is unspecified whether the allocation function is called before evaluating the constructor arguments or after evaluating the constructor arguments but before entering the constructor.

On top of that paragraph 17 of the same section insists that

If any part of the object initialization described above [Footnote: This may include evaluating a new-initializer and/or calling a constructor.] terminates by throwing an exception and a suitable deallocation function is found, the deallocation function is called to free the memory in which the object was being constructed... If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object's memory to be freed...

Now suppose we have:

  1. An implementation that always evaluates the constructor arguments first (for a new-expression that creates an object of a class type and has a new-initializer) and calls the allocation function afterwards.
  2. A class like this:
        struct  copy_throw  {
           copy_throw(const copy_throw&)
           {   throw  std::logic_error("Cannot copy!");   }
           copy_throw(long, copy_throw)
           {   }
           copy_throw()
           {   }
        };
    
  3. And a piece of code that looks like the one below:
        int  main()
        try  {
           copy_throw   an_object,     /* undefined behaviour */
              * a_pointer = ::new copy_throw(0, an_object);
           return  0;
        }
        catch(const std::logic_error&)
        {   }
    

Here the new-expression '::new copy_throw(0, an_object)' throws an exception when evaluating the constructor's arguments and before the allocation function is called. However, 7.6.2.8 [expr.new] paragraph 17 prescribes that in such a case the implementation shall call the deallocation function to free the memory in which the object was being constructed, given that a matching deallocation function can be found.

So a call to the Standard library deallocation function '::operator delete(void*)' shall be issued, but what argument is an implementation supposed to supply to the deallocation function? As per 7.6.2.8 [expr.new] paragraph 17 - the argument is the address of the memory in which the object was being constructed. Given that no memory has yet been allocated for the object, this will qualify as using an invalid pointer value, which is undefined behaviour by virtue of 6.7.5.5.3 [basic.stc.dynamic.deallocation] paragraph 4.

History
Date User Action Args
2022-02-18 07:47:23adminsetmessages: + msg6668
2014-03-03 00:00:00adminsetstatus: drwp -> cd3
2012-11-03 00:00:00adminsetstatus: dr -> drwp
2012-02-27 00:00:00adminsetmessages: + msg3791
2012-02-27 00:00:00adminsetstatus: ready -> dr
2011-09-06 00:00:00adminsetstatus: review -> ready
2008-05-18 00:00:00adminsetmessages: + msg1652
2008-05-18 00:00:00adminsetstatus: open -> review
2001-06-26 00:00:00admincreate