Thanks to @EdHeal for pointing out the basic idea behind the following approach:
Before your loop, within your block of memory "allocate" large-enough arrays of all the concrete data types you will need to use. Use these as your set of object pools. Unlike the typical implementation of object pools, you never have to grow your pools, because when you create the arrays at the start you you know an upper bound on the number of objects of each type that you might use. (Your problem statement implies that you have this information at least before entering the loop.)
During the loop, each time you need a new object, get its pointer from its pool. You can implement a function that puts the pointer back in the pool to be used as a "new" object again, or if there is a known limit on the total number of "new allocations" that isn't too much greater than the highwater number of objects in use at any one time, you don't even need to bother putting objects back in the pool. (The second option would make the pool management very easy, as all you have to do is keep count of the number of objects that have been taken from each pool, and that tells you which object is the next "new" one.) In either case, at the end of the loop, you reset the pools so that all objects are available to be "new" again.
I'd probably create a class called something like Allocator
to manage the pools, with a function for each kind of object it can take from the pool or put back in the pool and a reset()
function to call at the end of the loop. There may be more elegant ways to do the job.
Note that if you know the upper bounds of your required numbers of objects precisely enough, well enough in advance, you can create all of the above-mentioned data structures without putting anything on the heap.