A C++17 `std::allocator` implementation This is an implementation of C++17 std::allocator under the name my_std::allocator, with deprecated stuff omitted for simplicity.

// A implementation of C++17 std::allocator
// with deprecated members omitted

#include &lt;cstddef&gt;
#include &lt;new&gt;
#include &lt;type_traits&gt;

namespace my_std {

  template &lt;typename T&gt;
  class allocator {
  public:
    using value_type = T;
    using propagate_on_container_move_assignment = std::true_type;
    using is_always_equal = std::true_type;

    allocator() noexcept = default;
    allocator(const allocator&amp;) noexcept = default;
    ~allocator() = default;

    template &lt;class U&gt;
    allocator(const allocator&lt;U&gt;&amp;) noexcept
    {
    }

    T* allocate(std::size_t n)
    {
      if constexpr (alignof(T) &gt; __STDCPP_DEFAULT_NEW_ALIGNMENT__)
        return static_cast&lt;T*&gt;(
          ::operator new(n * sizeof(T), static_cast&lt;std::align_val_t&gt;(alignof(T)))
        );
      else
        return static_cast&lt;T*&gt;(
          ::operator new(n * sizeof(T))
        );
    }

    void deallocate(T* p, std::size_t n)
    {
      if constexpr (alignof(T) &gt; __STDCPP_DEFAULT_NEW_ALIGNMENT__)
        return ::operator delete(p, n * sizeof(T),
                                 static_cast&lt;std::align_val_t&gt;(alignof(T)));
      else
        return ::operator delete(p, n * sizeof(T));      
    }
  };

  template &lt;class T, class U&gt;
  bool operator==(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept
  {
    return true;
  }

  template &lt;class T, class U&gt;
  bool operator!=(const allocator&lt;T&gt;&amp;, const allocator&lt;U&gt;&amp;) noexcept
  {
    return false;
  }

}


I have tested it like this:

int main()
{
  std::set&lt;std::string, std::less&lt;std::string&gt;,
       my_std::allocator&lt;std::string&gt;&gt; words;

  for (std::string w; std::cin &gt;&gt; w;)
    words.insert(std::move(w));

  for (const auto&amp; w : words)
    std::cout &lt;&lt; w &lt;&lt; "\n";
}


And currently no bug is discovered.
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.