I would like to use different instances of an STL custom allocator class to manage different memory spaces, and then be able to specify an allocator instance to an STL container
The STL containers allow you to pass the allocator in as an argument to the constructor.
For example here are the appropriate constructors for vector:
explicit vector(const Allocator& = Allocator());
explicit vector(size_type n, const T& value = T(),
const Allocator& = Allocator());
template <class InputIterator>
vector(InputIterator first, InputIterator last,
const Allocator& = Allocator());
By default, they just use a default constructed allocator.
Unfortunately STL allocators cannot have state (or at least have to be very careful how that state is used) - each instance of a particular allocator type must be equivalent for STL containers to work effectively with them. I don't recall the details right now, but I know that Scott Meyers discusses this problem at length in "Effective STL", Item 10: Be aware of allocator conventions and restrictions.
However, you can have templated allocators that are very similar with the differences between the allocators being encapsulated in the allocator type and use different 'instantiations' of the allocator template (each template 'instantiation' is a different type). Again, my recollection is that Meyers discusses this pretty clearly.
For example see this paragraph from an article by Anthony Aue, "Improving Performance with Custom Pool Allocators for STL":
A potentially more serious caveat is that, since the allocator uses nonstatic data, it's not technically Standard compliant because the Standard requires that allocators of the same type be equivalent. See Effective STL (Item 10) for a thorough explanation of the issue. This amounts to requiring that an allocator for a given type be able to deallocate memory allocated by any other instance of an allocator for that type. For many uses of standard containers, this requirement is unnecessary (some might say Draconian). However, there are two cases where this requirement is absolutely necessary: list::splice and swap(). The case of swap() is especially serious because it is needed in order to implement certain operations on containers in an exception-safe manner (see Exceptional C++, Item 12). Technically, swap could be (and in some cases, is) implemented in the face of allocators that don't compare equally—items could be copied or the allocators could be swapped along with the data—but this is not always the case. For this reason, if you're using swap() or list::splice, you should make sure to use HoldingPolicySingleton; otherwise, you're bound to run into some really nasty behavior.
See also Stephan T. Lavavej's discussion in this newsgroup thread.
I'll update later tonight if someone else doesn't give the details in the meantime.
Perhaps you could code a set of allocator types which contains a static pointing to seperate memory spaces.
Then, when the STL container constructs its allocator, the allocator uses the memory spaceassigned to that allocator.
For simplicity, assume you want to use two memory spaces. Create two allocator types, one for each space. Pass the allocator type to the STL container constructors as required.