Container for fast deletion and addition of elements
struct Laugh {
  Laugh() {
    std::cout &lt;&lt; "Ha";
  }
};

int main() {
  ObjectPool&lt;Laugh&gt; pool(3);
}


Do you hear the evil laughter?

"active" vs the lifetime of an object

What happened?  When you resize the vector from its initial size 0 to some larger size in the constructor, then the vector will be filled with default-inserted objects.  So directly after construction of your object pool, it is already completely filled with alive objects.  These objects stay alive all the time (unless you resize).  Your notion of an "active" object basically just revolves around whether one can access (through at) that object.  "deleting" just moves one object out of the "active" (accessible) region of your pool.  "adding" enlarges this "active" region.

This is very confusing and not what one does expect from an "object pool"!

Further, it puts up some requirements to that Element type, namely that it must be default-insertable.  With the standard allocator (which you use) this means that Element has to be constructible through value initialization.  That's a pretty tough requirement, as it basically means that Element needs to have a zero argument constructor.  Now, if Element is a bit more than a simple value / data aggregation type, then its objects will probably depend on some objects of other types.  To follow the SRP one wouldn't let an Element construct the other objects then, but rather pass it the requirements upon its own construction (dependency injection).

Your object pool design does not allow this, though.  In short:  You sign yourself up for lots of future trouble.

To fix this, use reserve for your vector in the constructor, and actually construct new objects when adding (make emplace_back of the vector accessible) and destruct (terminology is important!) them when you want to get rid of them.  To keep that later operation fast, swap the last element and the one you want to get rid of and then erase the last. (Of course this only works when order doesn't matter.  Moreover, it invalidates possibly obtained references to the last element.)

Swapping done right

Calling std::swap directly is almost always wrong.  Bring it into scope with using std::swap;, and then just do an unqualified call: swap(a, b).  This allows ADL to kick in and find potentially useful user-defined swap functions.

Provide a complete interface

std::vector provides at, which - like yours - does bounds checking.  It also provides operator[] which does not.  Thus, I'd hope for (expect) your class to provide this feature, too.

Furthermore, I'd follow the terminology used by the standard std::vector for adding / removing elements here: push_back (or emplace_back) and erase.

Expose the allocator

std::vector allows to use a different allocator than the standard one.  You should expose this with your pool, too.  That allows for object pools whose objects have automatic storage duration ("are on the stack") when used with such an allocator.