Overloading operator new and exception correctness - c++

While looking at legacy code I found something similar to the following code
void* legacy_type::operator new(size_t size) {
return pool_alloc(size);
}
pool_alloc is known to never throw and return 0 in case of failure.
There is no overload for std::nothrow variant of new here.
I wonder whether this code is semantically correct and has well defined behavior.
Should new (std::nothrow) legacy_type; use custom pool_alloc? In my compiler it does not compile at all. Is it well defined behavior?
Should constructor run and crash due to this==0 if overloaded operator new returns zero? In my compiler it runs (and crashes on member initialization). Is it standard well defined behavior?

1) No it shouldn't. These are different functions. And, when you overload one of the operations - all other operations will never work, if they are not overloaded, since if there is operator new in class-scope and signature is not acceptable, then compiler will not search for global operator new, so, compile-error will happened.
2) You break postconditions, if your new will return null pointer.
n3376 18.6.1.1/3
Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else throw a bad_-
alloc exception. This requirement is binding on a replacement version of this function.
If you want to returns 0, you should use following signature
void* operator new(size_t) throw()
if you use this overloading and returns 0 from it, there is no seg-fault.
n3376 5.3.4/13
If the
allocation function returns null, initialization shall not be done, the deallocation function shall not be called,
and the value of the new-expression shall be null.

Related

C++ unusual use of placement new into NULL

In the following C++98 statement:
multiThreadService[nextBuffer] = new (NULL) MultiThreadService(binder);
Would it be correct to say:
this is "placement new",
the object will be created (at NULL, somehow?) and thrown away, and
multiThreadService[nextBuffer] will now be NULL?
I was also told this could be UB - is that the case?
First, it is not necessarily calling the global non-allocating placement-new operator new overload void* operator new(std::size_t size, void* ptr) (What is commonly meant by "placement new".)
Because the new-expression is not qualified as ::new, it may prefer an in-class overload of operator new if a suitable one exists and furthermore the type of NULL is not void*, but either std::nullptr_t or an integral type (the former not being possible in C++98). Therefore there are some potential alternative results of overload resolution even in the global case.
Searching the project for operator new overloads one can find at least one possible candidate at https://github.com/aneto0/MARTe2/blob/master/Source/Core/BareMetal/L2Objects/CLASSREGISTER.h#L85, which seems to return a valid pointer even if the placement argument (second function argument) is a null pointer.
If the selected overload is really the global non-allocating placement-new operator new, then it was basically a noop in C++98 resulting in a null pointer. new expressions were required to not do any initialization if operator new returns a null pointer.
However this was changed with CWG 1748 and now the behavior is undefined if the global non-allocating placement-new operator new returns a null pointer value. This may have been considered a defect against C++98 as well (I don't know), in which case -std=c++98 will not prevent the undefined behavior on a current compiler.

Is constructor called if nothrow new return nullptr?

If I use new and get std::bad_alloc compiler do not call constructor because of exception. But how does it works with the nothrow new cause we get pointer in every case? Is there special paragraph in standard for this case?
[expr.new]/16 ... if the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.

Using placement new in generic programming

When using placement new in generic code to construct an object at a specified address, the usage pattern is a bit different from usual code. For example, consider this implementation of uninitialized_copy: ([uninitialized.copy])
template <class It, class For>
For uninitialized_copy(It first, It last, For dest)
{
using T = typename std::iterator_traits<For>::value_type;
for (; first != last; ++first, (void)++dest)
::new (static_cast<void*>(std::addressof(*dest))) T(*first);
}
This post addresses the following points from the perspective of the standard:
why ::new is used instead of just new;
why an explicit cast to void* is required.
(This answer uses N4659, the final C++17 draft.)
Why ::new is used instead of just new
::new ensures that the operator new is looked up in the global scope. In contrast, the plain new first looks up in the scope of the class if T is a class type (or array thereof), and only then falls back to the global scope. Per [expr.new]/9:
If the new-expression begins with a unary ​::​ operator, the
allocation function's name is looked up in the global scope.
Otherwise, if the allocated type is a class type T or array thereof,
the allocation function's name is looked up in the scope of T. If
this lookup fails to find the name, or if the allocated type is not a
class type, the allocation function's name is looked up in the global
scope.
For example, with
struct C {
void* operator new(std::size_t, void* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}
};
The plain new will cause this function to be found, thus printing unwanted message, whereas ::new will still find the global function and work properly.
The global operator new(std::size_t, void*) cannot be replaced because of [new.delete.placement]/1:
These functions are reserved; a C++ program may not define functions that displace the versions in the C++ standard library
([constraints]). The provisions of [basic.stc.dynamic] do not apply
to these reserved placement forms of operator new and operator
delete.
(See How should I write ISO C++ Standard conformant custom new and delete operators? for more about overloading operator new.)
Why an explicit cast to void* is required
Although the global operator new(std::size_t, void*) may not be replaced, new versions of ::operator new can be defined. For example, suppose that the following declaration is placed in the global scope:
void* operator new(std::size_t, int* ptr) noexcept
{
std::cout << "Hello placement new!\n";
return ptr;
}
Then ::new(ptr) T will use this version instead of the global version, where ptr is a int* value. The pointer is explicitly cast to void* to ensure that the void* version of operator new (which we intend to call) wins in overload resolution.
From comment:
But why do we want to call exactly global new for void* if some
type has special overload of new for itself? Seems like normal
overloaded operator is more suitable - why it's not?
Normally, new is used for allocation purposes. Allocation is something the user should have control over. The user can roll out more suitable versions for a normal new.
In this case, however, we don't want to allocate anything — all we want to do is create an object! The placement new is more of a "hack" — its presence is largely due to the lack of syntax available for constructing an object at a specified address. We don't want the user to be able to customize anything. The language itself, however, doesn't care about this hack, though — we have to treat it specially. Of course, if we have something like construct_at (which is coming in C++20), we will use it!
Also note that std::uninitialized_copy is intended for the simplest case where you just want to copy construct a sequence of objects in raw allocated space. The standard containers allow you to customize not only how the elements are allocated, but also how they are constructed, by means of an allocator. Therefore, they do not generally use std::uninitialized_copy for their elements — they call std::allocator_traits<Allocator>::construct. This feature is used by std::scoped_allocator_adaptor.

Passing null pointer to placement new

The default placement new operator is declared in 18.6 [support.dynamic] ¶1 with a non-throwing exception-specification:
void* operator new (std::size_t size, void* ptr) noexcept;
This function does nothing except return ptr; so it is reasonable for it to be noexcept, however according to 5.3.4 [expr.new] ¶15 this means that the compiler must check it doesn't return null before invoking the object's constructor:
-15-
[Note: unless an allocation function is declared with a non-throwing exception-specification (15.4), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 15, 18.6.2.1); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
It seems to me that (specifically for placement new, not in general) this null check is an unfortunate performance hit, albeit small.
I've been debugging some code where placement new was being used in a very performance-sensitive code path to improve the compiler's code generation and the check for null was observed in the assembly. By providing a class-specific placement new overload that is declared with a throwing exception-specification (even though it can't possibly throw) the conditional branch was removed, which also allowed the compiler to generate smaller code for the surrounding inlined functions. The result of saying the placement new function could throw, even though it couldn't, was measurably better code.
So I've been wondering whether the null check is really required for the placement new case. The only way it can return null is if you pass it null. Although it's possible, and apparently legal, to write:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
I can't see why that would be useful, I suggest it would be better if the programmer had to check for null explicitly before using placement new e.g.
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Has anyone ever needed placement new to correctly handle the null pointer case? (i.e. without adding an explicit check that ptr is a valid memory location.)
I'm wondering whether it would be reasonable to forbid passing a null pointer to the default placement new function, and if not whether there is some better way to avoid the unnecessary branch, other than trying to tell the compiler the value is not null e.g.
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
Or:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
N.B. This question is intentionally tagged micro-optimisation, I am not suggesting that you go around overloading placement new for all your types to "improve" performance. This effect was noticed in a very specific performance-critical case and based on profiling and measurement.
Update: DR 1748 makes it undefined behaviour to use a null pointer with placement new, so compilers are no longer required to do the check.
While I can't see much of a question in there except "Has anyone ever needed placement new to correctly handle the null pointer case?" (I haven't), I think the case is interesting enough to spill some thoughts on the issue.
I consider the standard broken or incomplete wrt the placement new function and requirements to allocation functions in general.
If you look closely at the quoted §5.3.4,13, it implies that every allocation function has to be checked for a returned nullpointer, even if it is not noexcept. Therefore, it should be rewritten to
If the allocation function is declared with a non-throwing exception-specification and returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
That would not harm the validity of allocation functions throwing exceptions, since they have to obey §3.7.4.1:
[...] If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. [...] The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).
And §5.3.4,14:
[ Note: when the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. [...] -end note ]
Obviously, a placement new that just returns the given pointer, cannot reasonably check avilable storage size and alignment. Therefore,
§18.6.1.3,1 about placement new says
[...] The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.
(I guess they missed to mention §5.3.4,14 at that place.)
However, together these paragraphs say indirectly "if you pass a garbage pointer to the palcement functions, you get UB, because §5.3.4,14 is violated". So it's up to you to check the sanity of any poitner given to placement new.
In that spirit, and with the rewritten §5.3.4,13, the standard could strip the noexcept from placement new, leading to an addition to that indirect conclusion: "...and if you pass null, you get UB as well". On the other hand, its much less likely to have a misaligned pointer or pointer to too few memory than having a null pointer.
However, this would remove the need for checking against null, and it would fit well to the philosophy "don't pay for what you don't need". The allocation function itself would not need to check, because §18.6.1.3,1 explicitly says so.
To round things up, one could consider adding a second overload
void* operator new(std::size_t size, void* ptr, const std::nothrow_t&) noexcept;
Sadly, proposing this to the committee is unlikely to result in a change, because it would break existing code relying on placement new being ok with null pointers.

C++: anything wrong with the following code?

I got the the following code:
T *p = new T[10];
delete p;
It should be delete [] p, I think. I tried it on T=int type, no error. Then I created a simple class, exception. Any clear explanation?
T *p = 0;
delete p;
What's wrong with them? [I looked it up, it seems that although delete p will lead to error, but program resume because compiler thinks p==0 in the first place, so this error does not matter.]
Yes,
You should call delete [] p;
Any clear explanation?
It is undefined behavior to:
Call delete if allocation was through new []
Call delete on non dynamically allocated pointer.
Note that Undefined Behavior means that anything can happen, it does not mandate a crash. It simply means your program can show any behavior(including working as expected).
It is perfectly valid to call delete on a NULL pointer. The standard allows that. The delete operator internally takes core of that, the caller does not have to bother about the NULL check.
References:
C++03 standard §3.7.4.2-3:
If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation function is one supplied in the standard library, the call has no effect. Otherwise, the value supplied
to operator delete(void*) in the standard library shall be one of the values returned by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const std::nothrow_-t&) in the standard library, and the value supplied to operator delete[](void*) in the standard library shall be one of the values returned by a previous invocation of either operator new[](std::size_t) or
operator new[](std::size_t, const std::nothrow_t&) in the standard library.
When you allocate an array of objects with new[], you must delete it with delete[]. Failing to do so results in Undefined Behavior.
The main behavioral difference between delete and delete[] is that the latter, apart from deallocating the memory, also invokes the destructor of each object in the array.
Considering your second point:
T *p = 0;
delete p;
This will not cause an error, but rather do nothing, because that's the expected behavior of delete when the pointer is null.