Continue after container contracts and hash lookup. The workload is a live entity store: other systems retain pointers while unrelated entities are added or removed. We need stability during churn without assuming contiguous storage or queue order.
Name the backend being exercised
Native std::hive is unavailable in the installed standard library. This
lesson runs Matthew Bentley's pinned plf::hive 1.55.16 reference
implementation, commit 1e127fd2d202f33523691b2310eedf96a1998978, under its zlib
license. Include <plf_hive.h>; the grader and editor expose that exact header.
No namespace alias pretends it is std::hive, and the native feature probe remains
unavailable. The common storage and lifetime ideas transfer, but passing this
practice is not evidence of native-library availability or full conformance.
The C++26 hive design groups elements into blocks and tracks erased slots. Insertion position is unspecified so space can be reused. Existing non-erased element iterators and references survive ordinary insert/erase operations, but an erased element's handles are invalid. Other operations have their own invalidation rules: do not generalize this promise to sort, assignment, capacity-changing operations, replacement or a cached past-the-end iterator.
An empty element block may be retired or reserved for reuse; do not promise that every empty block is retained. Exact block sizes, growth and address-reuse policies are implementation details. Iterators are bidirectional, not random access. Insertion/erasure are efficient for this workload, but allocation and locality tradeoffs still require measurements on the intended data.
A stable pointer does not identify a future replacement object
Do not dereference a pointer after its entity is erased. Slot reuse can create an address-equality trap: the address may be numerically the same while the lifetime/identity contract changed. Use stable ids and reacquire a pointer after lookup; use generations when an application must distinguish reused handles. This exercise allows id reuse only after deletion and returns live pointers; it does not provide a stale-handle detection API.
#include <plf_hive.h>
#include <cstdio>
int main() {
plf::hive<int> values;auto first=values.insert(7);int* retained=&*first;
for(int i=0;i<2000;++i)values.insert(i);
auto other=values.insert(99);values.erase(other);
std::printf("plf::hive reference: stable %d, value %d\n",&*first==retained,*retained);
}This program uses reference iterators deliberately. It does not print exact block counts, address order or insertion order. Those are not the contract.
Keep the id index consistent with storage
An unordered_map can map entity ids to live hive iterators. Rehashing the index changes its own iterators, not the entity locations stored in the hive. Every erase must retire the corresponding id entry. Duplicates must be rejected before creating a second entity with the same key.
During filtering, save the id before destroying the entity and continue from the iterator returned by erase. Incrementing an erased iterator is invalid. Return removed ids in sorted order if the public API requires deterministic output; do not equate hive iteration order with chronology.
A default copy of a store containing iterators would copy references into the original store. The build keeps EntityStore noncopyable. A production copy operation would need to build a fresh index into the copied storage, and move operations would need their own allocator and iterator analysis. Those are separate contracts, not incidental defaults.
The graded exercise checks growth, unrelated erasure, consecutive filtered removals, id reuse, pointer identity, empty/refilled storage and agreement with a map model. Its actual pinned library runs under ASan/UBSan/LSan. The standard hive API can be evaluated separately when the installed library supplies it; this backend remains explicitly named and reproducible.