11.05 In-Place Vector Compaction, Iterator Invalidation, and C++20 std::erase
std::remove does not alter container size, analyze iterator invalidation hazards during array filtering, and review C++20 std::erase.
💻 1. Annotated Source Code
#include <iostream> #include <vector> #include <algorithm> using namespace std; void printVector(const vector<int>& vec); int main() { vector<int> numbers = { 1, 2, 3, 2, 4, 2, 5, 2 }; cout << "Original vector: "; printVector(numbers); //step 1: use remove auto newEnd = remove(numbers.begin(), numbers.end(), 2); //step 2: erase them from container numbers.erase(newEnd, numbers.end()); cout << "Vector after removing all 2s: "; printVector(numbers); return 0; } void printVector(const vector<int>& vec) { for (int value : vec) { cout << value << " "; } cout << endl; }
📐 2. Architecture & UML Class Model
📚 3. Core C++ Concepts Deep-Dive
1. The Two-Step Remove-Erase Idiom
In standard C++, std::remove only shifts non-matching elements to the front of the range and returns a past-the-end iterator. It cannot alter the container's size() because generic algorithms operate only on iterators without knowledge of container topology. The container's erase() method must be called to truncate the tail.
vec.erase(std::remove(vec.begin(), vec.end(), targetValue), vec.end());
⚡ 4. Embedded Systems & Hardware Reality
1. Deterministic In-Place Compaction
The remove-erase idiom operates strictly in-place with $O(N)$ linear time and $O(1)$ auxiliary space. In embedded sensor filtering (e.g. stripping corrupted checksum packets from an acquisition buffer), in-place compaction avoids allocating secondary temporary buffers.
⚠️ Iterator Invalidation Hazards
Calling erase() invalidates iterators pointing to deleted and subsequent elements. Performing manual loops with vec.erase(it) without updating it = vec.erase(it) results in undefined memory dereferences.
💡 5. Production-Ready Embedded Refactoring
In modern C++20, the verbose two-step idiom is replaced with uniform, clear std::erase:
#include <vector> #include <iostream> int main() { std::vector<int> sensorReadings = {10, -999, 25, -999, 32}; // Modern C++20: Single expressive call std::erase(sensorReadings, -999); for (int val : sensorReadings) { std::cout << val << ' '; // Prints: 10 25 32 } return 0; }
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
std::remove moves valid elements to the front and returns an iterator to the new logical end, requiring vec.erase() to deallocate the remaining tail.