So far, the answer seems to involve referring to boost implementation details, which doesn't thrill me.
I just wanted to create some memory-pools that could be used to allocate objects referred to through boost:shared_ptr<>.
I want to avoid non-pool memory-allocation, and for each shared-object allocation to involve one (and only one) allocation from one pool, i.e. to be as predictable and compact as possible.

This hand-written definition for a pool and an allocator seem to do what I want:

----------

template<typename T>
class my_pool
{
private:
  char *rawStorage;
  T *storage;
  T *nextFree;
public:
  my_pool(size_t S)
    : storage(nullptr), nextFree(nullptr)
  {
    // Allocate memory.
    rawStorage = new char[sizeof(T) * S];
    storage = reinterpret_cast<T*>( rawStorage );

    // All objects are free, initially.
    for (size_t i = 0; i < S; ++i)
    {
      T **pFreeObject = reinterpret_cast<T**>(&storage[i]);
      *pFreeObject = nextFree;
      nextFree = reinterpret_cast<T*>(&storage[i]);
    }
  }

  ~my_pool()
  {
    // TODO: Verify that all allocated objects have been freed.
    delete[] rawStorage;
  }

  T *allocate()
  {
    T *freeObject = nextFree;
    if (nextFree != nullptr)
    {
      T **pFreeObject = reinterpret_cast<T**>(nextFree);
      nextFree = *pFreeObject;
    }
    return freeObject;
  }

  void deallocate(T *obj)
  {
    T **pFreeObject = reinterpret_cast<T**>(obj);
    *pFreeObject = nextFree;
    nextFree = obj;
  }
};

template<typename T>
class my_allocator : public at_boost::detail::sp_ms_deleter<T>
{
public:
  typedef at_boost::detail::sp_counted_impl_pda<T *, at_boost::detail::sp_ms_deleter<T>, my_allocator<T> > shared_type;
  typedef my_pool<shared_type> pool_type;
private:
  pool_type &m_rPool;
  void operator=(my_allocator const &other); // Disallow assignment.
public:
  explicit my_allocator(pool_type &a_rPool) : m_rPool(a_rPool) { }
  my_allocator(my_allocator const &other) : m_rPool(other.m_rPool) { }

  template <typename U>
  struct rebind
  {
    typedef my_allocator/*<U>*/ other;
  };

  shared_type *allocate(int iCount)
  {
    if (iCount == 1) // Only expected to allocate single objects.
      return m_rPool.allocate();
    return nullptr;
  }
  void deallocate(shared_type *obj, size_t iCount)
  {
    m_rPool.deallocate(obj);
  }
};

----------

It still needs polish, but it's just a proof of concept.

Here's some code that uses the pool/allocator:

----------
my_allocator<int>::pool_type myPool (1024);
my_allocator<int> myAllocator(myPool);
boost::shared_ptr<int> pTest = boost::allocate_shared<int, my_allocator<int> >(myAllocator);
int iVal0 = *pTest;
*pTest = sizeof(my_allocator<int>::shared_type);
int iVal1 = *pTest;
----------

No big deal. Mostly, the test is to make sure that ::operator new() doesn't get called after the construction of the pool, and that seems to be true.

I'm surprised that my expected usage of memory-pools isn't more common.
Maybe using boost in an environment with constraints on dynamic memory allocation is unusual?

Also, at some point, I need to figure out why my definition of my_allocator<T>::rebind can't use <U> in the way that the standard boost allocators do.
I get an incomprehensible error-message when I do that:

----------
boost\smart_ptr\detail\sp_counted_impl.hpp(237): error C2664: 'my_allocator<T>::my_allocator(my_pool<at_boost::detail::sp_counted_impl_pda<P,D,A>> &)' : cannot convert parameter 1 from 'my_allocator<T>' to 'my_pool<T> &'
----------

Any comments on what I'm trying to do here?

-Steven