Deriving from concrete, non-polymorphic, classes is typically a bad idea. If a class doesn't have a virtual destructor, it wasn't designed with inheritance in mind (compare to inheritable standard library classes like streams, buffers, and facets)
In this case, because you don't provide rebind, parent class rebind is selected, and parent (std::allocator<K,V>) is used for all allocations done by a container that's instantiated with this PoolAllocator<K,V>.
I don't see any checks to ensure "PoolAllocator::bytes's" alignment. Most modern operating systems try to return aligned pointers, but this is not always the case. On some CPUs, attempting to read misaligned data can cause a complete failure of the CPU which requires a restart. Granted, the CPUs we have now are capable of handling misaligned addresses, but it's not without a performance penalty. If you want performance, align your data.
Based on what I've seen in the implementation of "std::malloc_allocator" and "std::new_allocator", neither assure alignment of the allocated memory, which leads me to believe that the implementers of the aforementioned classes assume the OS will align the memory for them.
In your implementation of "PoolAllocator::deallocate( )", why are you throwing exceptions left, right and centre? Throwing exceptions is expensive and you should only throw them when something really has gone wrong. I personally think you're misusing exceptions in your code.
Also, what's the purpose of "PoolAllocator::max_size( )"?
both malloc() and operator new() assure alignment.
malloc() (quoted from C99 7.20.3/1)
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object
operator new() (quoted from C++11 ยง18.6.1.1[new.delete.single]/1)
The allocation function (3.7.4.1) called by a new-expression (5.3.4) to allocate size bytes of storage suitably aligned to represent any object of that size.
and in 3.7.4.1
The pointer returned shall be suitably aligned so that it can be converted
to a pointer of any complete object type with a fundamental alignment requirement
Actually, alignment is not always respected even though the standard says so due to compiler bugs. In addition, even if "new" and "malloc( )" do return aligned memory, it's not guaranteed to be sufficiently aligned for the program's needs; alignment to a CPU cache-line springs to mind.
Such fundamental bugs are rare, but need to be reported in any case.
Over-aligned types do need special care (and special allocators if they are placed in a container), but this isn't the case.
How do we know that Catfish's allocator is not a special case? We don't know what sort of data is going to be stored. In addition, the allocator needs respect special alignment requirements for types that it may be required to store at some point in the future if it [the allocator] has any chance of being flexible and reusable.