I'm assuming you want provide a custom, class-specific allocation function and not overload the new operator in the global namespace.
tl;dr
Since C++11: Declare your custom operator new with noexcept and everything works fine.
void* T::operator new (size_t size) noexcept;
Full answer
While the currently accepted answer given by John proposes a working solution, it implies that the std::nothrow tag is necessary and this can't be done without it, which is wrong.
This answer is just the (correct) comment of @dyp, expanded into a full answer and with current quotes from the standard.
The C++11 standard says in [basic.stc.dynamic.allocation], paragraph 3:
If an allocation function declared with a non-throwing exception-specification (15.4) fails to allocate storage, it shall return a null pointer. Any other allocation function that fails to allocate storage shall indicate failure only by throwing an exception of a type that would match a handler (15.3) of type std::bad_alloc (18.6.2.1).
Over the years, this sentence has been rewritten, but something semantically equivalent has been in C++14, C++17 and C++20. The current working draft for C++23 has this paragraph in [basic.stc.dynamic.allocation]:
An allocation function that has a non-throwing exception specification (14.5) indicates failure by returning a null pointer value. Any other allocation function never returns a null pointer value and indicates failure only by throwing an exception (14.2) of a type that would match a handler (14.4) of type std::bad_alloc (17.6.4.1).
The "non-throwing exception specification" is defined in [except.spec]. A function has this specification if you add noexcept as a suffix to the declaration. Thus, the code given in the question can work totally fine, but the custom new operator has to be declared with noexcept:
void* T::operator new (size_t size) noexcept;
Alternatively, up to C++17, using throw() instead of noexcept worked too, but it has been deprecated in C++11 and removed in C++20. It was the way to go before C++11. OP ended up using this according to their own answer.
operator newandoperator new[]functions only allocate memory, it's the compiler which creates the code to call the possible constructor when you use thenewoperator. Returning a null pointer (nullptror alternatively0) is the correct behavior on failure.noexceptorthrow(), see [basic.stc.dynamic.allocation]p3 -- it is not clear to me if that's implied when disabling exceptions.