From item 11 of section 16.5.3.5 Cpp17Allocator requirements of n4861.pdf:
"The following is an allocator class template supporting the minimal interface...":
template<class Tp>
struct SimpleAllocator {
typedef Tp value_type;
SimpleAllocator(ctor args);
template<class T> SimpleAllocator(const SimpleAllocator<T>& other);
[[nodiscard]] Tp* allocate(std::size_t n);
void deallocate(Tp* p, std::size_t n);
};
template<class T, class U>
bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
template<class T, class U>
bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
This minimalness works because of the user specializable std::allocator_traits
class template which "supplies a uniform interface to all allocator types" (from item 2 of the aforementioned section).
I.e. with just the above definitions, this class template basically fills in the rest of what actually gets used (by conforming types that can take an allocator template parameter).
Going beyond the minimal allocator,
one can look at LLVM's latest fully defined standard C++ library implementation of std::allocator
.
To see a C++17 based custom allocator that I've written, see ThreadLocalAllocator
.
These are both examples of what's being categorized as "stateless" allocators.
For a gist of mine on the categorization of allocators this way,
see C++: Stateless vs. Stateful Allocators.