A C++17 `std::allocator` implementation
noexcept on the explicitly defaulted default-ctor and copy-ctor is already implied.
Copy-ctor and dtor would be implicitly declared if left out.
.allocate() (and also .deallocate(), but that doesn't matter) fails to check n * sizeof(T) for wrap-around.
Possible Solutions:


Start with a check. This has the advantage of simplicity and broadest conformity.

if (n &gt; std::size_t(-1) / sizeof(T))
    throw std::bad_array_new_length();

Use saturating math, and rely on ::operator new being unable to allocate std::size_t(-1) bytes on (nearly?) all implementations. This is generally more efficient for the common case.

auto bytes = n &gt; std::size_t(-1) / sizeof(T) ? std::size_t(-1) : n * sizeof(T);


Because the casts are so simple and obviously right, I would eschew the verbosity of static_cast. Guidelines may prohibit it though...
I suggest importing the following additional refinements from C++20:


Mark the templated ctor constexpr.
Mark .allocate()'s return-value [[nodiscard]].
Mark both operator== and operator!= constexpr.