Section: 17.6.3.2 [new.delete.single] Status: CD1 Submitter: Howard Hinnant Opened: 1999-08-29 Last modified: 2016-01-28
Priority: Not Prioritized
View other active issues in [new.delete.single].
View all other issues in [new.delete.single].
View all issues with CD1 status.
Discussion:
As specified, the implementation of the nothrow version of operator new does not necessarily call the ordinary operator new, but may instead simply call the same underlying allocator and return a null pointer instead of throwing an exception in case of failure.
Such an implementation breaks code that replaces the ordinary version of new, but not the nothrow version. If the ordinary version of new/delete is replaced, and if the replaced delete is not compatible with pointers returned from the library versions of new, then when the replaced delete receives a pointer allocated by the library new(nothrow), crash follows.
The fix appears to be that the lib version of new(nothrow) must call the ordinary new. Thus when the ordinary new gets replaced, the lib version will call the replaced ordinary new and things will continue to work.
An alternative would be to have the ordinary new call new(nothrow). This seems sub-optimal to me as the ordinary version of new is the version most commonly replaced in practice. So one would still need to replace both ordinary and nothrow versions if one wanted to replace the ordinary version.
Another alternative is to put in clear text that if one version is replaced, then the other must also be replaced to maintain compatibility. Then the proposed resolution below would just be a quality of implementation issue. There is already such text in paragraph 7 (under the new(nothrow) version). But this nuance is easily missed if one reads only the paragraphs relating to the ordinary new.
N2158 has been written explaining the rationale for the proposed resolution below.
Proposed resolution:
Change 18.5.1.1 [new.delete.single]:
void* operator new(std::size_t size, const std::nothrow_t&) throw();-5- Effects: Same as above, except that it is called by a placement version of a new-expression when a C++ program prefers a null pointer result as an error indication, instead of a
bad_alloc
exception.-6- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-7- Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else return a null pointer. This nothrow version of operator new returns a pointer obtained as if acquired from the (possibly replaced) ordinary version. This requirement is binding on a replacement version of this function.
-8- Default behavior:
- Calls
operator new(size)
.- If the call to
operator new(size)
returns normally, returns the result of that call, else- if the call to
operator new(size)
throws an exception, returns a null pointer.Executes a loop: Within the loop, the function first attempts to allocate the requested storage. Whether the attempt involves a call to the Standard C library functionmalloc
is unspecified.Returns a pointer to the allocated storage if the attempt is successful. Otherwise, if the last argument toset_new_handler()
was a null pointer, return a null pointer.Otherwise, the function calls the current new_handler (18.5.2.2). If the called function returns, the loop repeats.The loop terminates when an attempt to allocate the requested storage is successful or when a called new_handler function does not return. If the called new_handler function terminates by throwing abad_alloc exception
, the function returns a null pointer.-9- [Example:
T* p1 = new T; // throws bad_alloc if it fails T* p2 = new(nothrow) T; // returns 0 if it fails--end example]
void operator delete(void* ptr) throw();void operator delete(void* ptr, const std::nothrow_t&) throw();-10- Effects: The deallocation function (3.7.4.2) called by a delete-expression to render the value of
ptr
invalid.-11- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-12- Requires: the value of
ptr
is null or the value returned by an earlier call to thedefault(possibly replaced)operator new(std::size_t)
oroperator new(std::size_t, const std::nothrow_t&)
.-13- Default behavior:
- For a null value of
ptr
, do nothing.- Any other value of
ptr
shall be a value returned earlier by a call to the defaultoperator new
, which was not invalidated by an intervening call tooperator delete(void*)
(17.4.3.7). For such a non-null value ofptr
, reclaims storage allocated by the earlier call to the defaultoperator new
.-14- Remarks: It is unspecified under what conditions part or all of such reclaimed storage is allocated by a subsequent call to
operator new
or any ofcalloc
,malloc
, orrealloc
, declared in<cstdlib>
.void operator delete(void* ptr, const std::nothrow_t&) throw();-15- Effects: Same as above, except that it is called by the implementation when an exception propagates from a nothrow placement version of the new-expression (i.e. when the constructor throws an exception).
-16- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-17- Requires: the value of
ptr
is null or the value returned by an earlier call to the (possibly replaced)operator new(std::size_t)
oroperator new(std::size_t, const std::nothrow_t&)
.-18- Default behavior: Calls
operator delete(ptr)
.
Change 18.5.1.2 [new.delete.array]
void* operator new[](std::size_t size, const std::nothrow_t&) throw();-5- Effects: Same as above, except that it is called by a placement version of a new-expression when a C++ program prefers a null pointer result as an error indication, instead of a
bad_alloc
exception.-6- Replaceable: a C++ program can define a function with this function signature that displaces the default version defined by the C++ Standard library.
-7- Required behavior:
Same as for operatorReturn a non-null pointer to suitably aligned storage (3.7.4), or else return a null pointer. This nothrow version of operator new returns a pointer obtained as if acquired from the (possibly replaced)new(std::size_t, const std::nothrow_t&)
. This nothrow version of operatornew[]
returns a pointer obtained as if acquired from the ordinary version.operator new[](std::size_t size)
. This requirement is binding on a replacement version of this function.-8- Default behavior:
Returnsoperator new(size, nothrow)
.
- Calls
operator new[](size)
.- If the call to
operator new[](size)
returns normally, returns the result of that call, else- if the call to
operator new[](size)
throws an exception, returns a null pointer.void operator delete[](void* ptr) throw(); void operator delete[](void* ptr, const std::nothrow_t&) throw();-9- Effects: The deallocation function (3.7.4.2) called by the array form of a delete-expression to render the value of
ptr
invalid.-10- Replaceable: a C++ program can define a function with this function signature that displaces the default version defined by the C++ Standard library.
-11- Requires: the value of
ptr
is null or the value returned by an earlier call tooperator new[](std::size_t)
oroperator new[](std::size_t, const std::nothrow_t&)
.-12- Default behavior: Calls
operator delete(ptr)
oroperator delete[](ptr
respectively., std::nothrow)
Rationale:
Yes, they may become unlinked, and that is by design. If a user replaces one, the user should also replace the other.
[ Reopened due to a gcc conversation between Howard, Martin and Gaby. Forwarding or not is visible behavior to the client and it would be useful for the client to know which behavior it could depend on. ]
[ Batavia: Robert voiced serious reservations about backwards compatibility for his customers. ]