Parent
Part of #5. Depends on #71, #72, #74.
Summary
Audit and enforce exception safety guarantees across all SmallVector operations, with specific handling for the heap transition — the most dangerous point for correctness.
Guarantees to provide
| Operation |
Guarantee |
push_back / emplace_back |
Strong — if T's ctor throws, SmallVector unchanged |
insert / emplace |
Strong if no reallocation; Basic if reallocation needed |
erase |
No-throw if T's move/dtor is no-throw |
reserve |
Strong — old storage untouched until move succeeds |
| Heap transition |
Strong — allocate new block, move all elements, then destroy old |
| Copy constructor |
Strong |
| Move constructor |
No-throw for heap mode; Basic for inline mode if T's move throws |
The heap transition in detail
This is the riskiest operation:
void grow() {
size_t new_cap = capacity_ * 2;
T* new_heap = static_cast<T*>(::operator new(new_cap * sizeof(T)));
// Move elements — if any move throws, we must clean up new_heap
size_t moved = 0;
try {
for (; moved < size_; ++moved)
new(new_heap + moved) T(std::move(data()[moved]));
} catch (...) {
// Destroy successfully moved elements
for (size_t i = 0; i < moved; ++i)
new_heap[i].~T();
::operator delete(new_heap);
throw; // re-throw, original storage untouched
}
// Destroy originals and switch to heap
for (size_t i = 0; i < size_; ++i)
data()[i].~T();
if (!is_small()) ::operator delete(heap_ptr);
heap_ptr = new_heap;
capacity_ = new_cap;
}
If T's move constructor is noexcept, the try/catch can be skipped entirely (compiler will optimise this with if constexpr (std::is_nothrow_move_constructible_v<T>)).
Testing requirements
- Push into a type whose copy constructor throws on the Nth call — SmallVector must be unchanged
- Heap transition with a throwing move type — old data preserved
- Verify
noexcept propagation: push_back is noexcept when T's move is noexcept
Parent
Part of #5. Depends on #71, #72, #74.
Summary
Audit and enforce exception safety guarantees across all
SmallVectoroperations, with specific handling for the heap transition — the most dangerous point for correctness.Guarantees to provide
push_back/emplace_backinsert/emplaceerasereserveThe heap transition in detail
This is the riskiest operation:
If
T's move constructor isnoexcept, the try/catch can be skipped entirely (compiler will optimise this withif constexpr (std::is_nothrow_move_constructible_v<T>)).Testing requirements
noexceptpropagation:push_backisnoexceptwhen T's move isnoexcept