I have been tossing this idea in my mind for almost 20 years. The current schema of overloading operator new belongs to dinosaur era IMO.
If we could provide operator new with the requested type - instead of just its size (and optionally alignment), then the return type could also be a custom allocator (rather just void*). This in turn would enable the new expression to return a smart pointer - rather than a dangerous raw pointer:

smart_allocator operator new(compiler_provided_params,user_provided_params) value_type { /*...*/};

The corresponding new expression:

auto ptr=new(user_provided_args) value_type {initializer_args};
Would then generate pseudo code sequence like this:

auto ptr = [&}(){
    smart_allocator temp_alloc{ operator new(compiler_provided_args,user_provided_args) value_type };
    smart_pointer ret=temp_alloc(initializer_args);
   return std::move(ret);

The exact type and number of compiler provided parameters is open to discussion, but it must at least provide the following functionality:

auto constructor_lambda(compiler_provided_params){
    return [](void * void_this, auto && ... initializer){
        ::new(__none_overloadable_placement__ ,  void_this) value_type {std::forward<decltype(initializer)>(initializer) ... };

the multi-lambda in the above pseudo snippet shall correspond to all accessible overloads of constructors of value_type in the context of calling new-expression. other functionality such as destructor accessibility and assignment operator overload set may also be considered, but avialablity of accessible constructors is of prime essence IMHO. 
This proposition is aiming at encouraging the usage of raw pointers as observer-only idiom. I am trying to combine the syntax clarity of new with safety of make_smart family of convenience functions. Other benefits include:
  • passing the whole type - instead of just its size - to the operator new enables safely conveying meta-data about exact type of object to runtime such as clonabilty true destructor. One problem with traditional new-expression is destructor slicing of none-polymorphic hierarcies (that this proposition can help avoid):
std::unique_ptr<base> { new derived };// dtor is sliced if base does not declare it as virtual
smarter_unique_ptr<base> { new(smart_tag) derived }; // can avoid destructor slicing if uses smart overloading for new
  • Custom return type of new enables optimizing data structure of smart pointers; e.g it is possible to embed the count of references into the same block of allocation as the actual object, enabling superfast reference counting.
  • Prohibit lots of issues (double-delete, dangling ptr, memory leak ...) resulting from improper pointer initialization.