Project Overview
This advanced capstone project challenges you to build a professional-grade linked list library that matches
the quality and performance of the C++ Standard Template Library (STL). You will work with the
Algorithms and their Complexities dataset from Kaggle containing algorithm complexity data
for testing insertion, deletion, traversal, and search operations across varying data sizes (100 to 1,000,000 elements).
The dataset includes timing expectations, memory usage baselines, and comparison metrics against std::list,
std::forward_list, and std::vector. Your library must demonstrate STL compatibility,
memory efficiency, and performance optimization through custom allocators and cache-aware design.
Data Structure
Singly and doubly linked list implementations
Iterators
Forward, bidirectional, and const iterators
Memory Optimization
Custom allocators and memory pooling
Benchmarking
Performance testing and optimization
Learning Objectives
Technical Skills
- Implement generic data structures with templates
- Design STL-compatible iterators
- Create custom memory allocators
- Apply move semantics and perfect forwarding
- Write exception-safe code with strong guarantees
Performance Skills
- Analyze time and space complexity
- Profile and benchmark code performance
- Optimize for cache locality
- Reduce memory fragmentation
- Compare against STL implementations
Project Scenario
CoreLib Systems
You have been hired as a Systems Developer at CoreLib Systems, a company
that develops high-performance C++ libraries for embedded systems and game engines. The company needs
a custom linked list implementation that can be used in memory-constrained environments where the
standard std::list may have too much overhead. The library must be fully STL-compatible
so existing codebases can easily adopt it.
"We need a linked list library that's as easy to use as std::list but more memory-efficient and predictable in performance. It should work seamlessly with STL algorithms, support custom allocators for our memory pools, and give us control over cache behavior. Can you build a library that matches STL's interface while outperforming it in our specific use cases?"
Functional Requirements
- push_front, push_back, pop_front, pop_back
- insert, erase, clear operations
- front, back element access
- size, empty, swap utilities
- begin(), end(), cbegin(), cend()
- rbegin(), rend() for doubly linked
- Forward iterator for singly linked
- Bidirectional iterator for doubly linked
- Work with std::find, std::sort algorithms
- Range-based for loop support
- Custom allocator template parameter
- Type traits and concepts (C++20)
- splice, merge, reverse operations
- unique, remove_if algorithms
- Move constructor and assignment
- emplace_front, emplace_back support
Benchmark Data
Use the comprehensive benchmark dataset to validate your linked list implementation's correctness and measure its performance against STL containers:
Dataset Download
Download the benchmark data files and operation sequences. Use these to test correctness and measure performance against baseline metrics.
Original Data Source
This project uses benchmark data inspired by the Algorithms and their Complexities dataset from Kaggle - containing algorithms with time/space complexity analysis and code implementations. The dataset includes complexity data for various data structure operations useful for performance testing.
Dataset Schema
| Column | Type | Description |
|---|---|---|
test_id | Integer | Unique test identifier (1-10000) |
operation | String | Operation type (push_front, push_back, insert, erase, traverse, find) |
data_size | Integer | Number of elements (100, 1000, 10000, 100000, 1000000) |
position | String | Position indicator (front, back, middle, random) |
expected_time_us | Float | Expected execution time in microseconds |
std_list_time_us | Float | std::list baseline time in microseconds |
complexity | String | Expected time complexity (O(1), O(n), O(log n)) |
category | String | Test category (insertion, deletion, access, algorithm) |
| Column | Type | Description |
|---|---|---|
sequence_id | Integer | Unique sequence identifier (1-500) |
operations | String | Comma-separated operation sequence |
initial_values | String | Initial list values (semicolon-separated) |
expected_result | String | Expected final list state |
test_type | String | Test type (correctness, performance, stress) |
| Column | Type | Description |
|---|---|---|
container | String | Container type (std::list, std::forward_list, std::vector, std::deque) |
operation | String | Operation type |
data_size | Integer | Number of elements |
avg_time_us | Float | Average execution time (μs) |
min_time_us | Float | Minimum execution time (μs) |
max_time_us | Float | Maximum execution time (μs) |
memory_bytes | Integer | Memory usage in bytes |
cache_misses | Integer | L1 cache misses count |
| Column | Type | Description |
|---|---|---|
element_type | String | Data type (int, double, string, custom_struct) |
element_count | Integer | Number of elements in list |
std_list_bytes | Integer | Memory usage by std::list |
target_bytes | Integer | Target memory usage for your implementation |
node_overhead | Integer | Per-node memory overhead in bytes |
allocator_type | String | Allocator type (default, pool, arena) |
Sample Data Preview
Here is what typical benchmark records look like from linked_list_benchmark_data.csv:
| test_id | operation | data_size | position | expected_time_us | std_list_time_us | complexity |
|---|---|---|---|---|---|---|
| 1 | push_front | 10000 | front | 0.15 | 0.18 | O(1) |
| 2 | push_back | 10000 | back | 0.15 | 0.19 | O(1) |
| 3 | insert | 10000 | middle | 25.4 | 28.7 | O(n) |
| 4 | traverse | 100000 | full | 1250.0 | 1340.5 | O(n) |
Project Requirements
Your linked list library must include all of the following components. Structure your implementation with clear separation between core data structure, iterators, and allocators.
Core Linked List Classes
Singly Linked List (forward_list):
- Node structure with data and next pointer
- push_front, pop_front - O(1) operations
- insert_after, erase_after - positional operations
- Forward iterator support only
Doubly Linked List (list):
- Node structure with data, prev, and next pointers
- push_front, push_back, pop_front, pop_back - O(1)
- insert, erase at any position with iterator
- Bidirectional iterator support
template<typename T, typename Allocator = std::allocator<T>>
class LinkedList {
public:
// Type aliases for STL compatibility
using value_type = T;
using allocator_type = Allocator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = typename std::allocator_traits<Allocator>::pointer;
using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
// Iterator types
class iterator;
class const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// Constructors, destructor, assignment
LinkedList();
explicit LinkedList(const Allocator& alloc);
LinkedList(size_type count, const T& value, const Allocator& alloc = Allocator());
LinkedList(std::initializer_list<T> init, const Allocator& alloc = Allocator());
LinkedList(const LinkedList& other);
LinkedList(LinkedList&& other) noexcept;
~LinkedList();
LinkedList& operator=(const LinkedList& other);
LinkedList& operator=(LinkedList&& other) noexcept;
// Element access
reference front();
reference back();
// Iterators
iterator begin() noexcept;
iterator end() noexcept;
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
// Capacity
bool empty() const noexcept;
size_type size() const noexcept;
// Modifiers
void clear() noexcept;
iterator insert(const_iterator pos, const T& value);
iterator erase(const_iterator pos);
void push_front(const T& value);
void push_back(const T& value);
void pop_front();
void pop_back();
// Emplace operations
template<typename... Args>
iterator emplace(const_iterator pos, Args&&... args);
template<typename... Args>
reference emplace_front(Args&&... args);
template<typename... Args>
reference emplace_back(Args&&... args);
private:
struct Node {
T data;
Node* prev;
Node* next;
template<typename... Args>
Node(Args&&... args) : data(std::forward<Args>(args)...), prev(nullptr), next(nullptr) {}
};
Node* head_;
Node* tail_;
size_type size_;
Allocator allocator_;
};
linked_list.hpp and forward_list.hpp
with full template implementations.
Iterator Implementation
Requirements for STL-compatible iterators:
- Forward iterator for singly linked list (InputIterator, OutputIterator, ForwardIterator)
- Bidirectional iterator for doubly linked list (adds BidirectionalIterator)
- Both const and non-const versions
- Proper iterator_traits specialization
- Support for range-based for loops
// Iterator class (nested in LinkedList)
class iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
iterator() : node_(nullptr) {}
explicit iterator(Node* node) : node_(node) {}
reference operator*() const { return node_->data; }
pointer operator->() const { return &(node_->data); }
iterator& operator++() {
node_ = node_->next;
return *this;
}
iterator operator++(int) {
iterator tmp = *this;
++(*this);
return tmp;
}
iterator& operator--() {
node_ = node_->prev;
return *this;
}
iterator operator--(int) {
iterator tmp = *this;
--(*this);
return tmp;
}
bool operator==(const iterator& other) const { return node_ == other.node_; }
bool operator!=(const iterator& other) const { return node_ != other.node_; }
private:
Node* node_;
friend class LinkedList;
};
List Operations
Implement the following list-specific operations:
- splice: Transfer elements from another list without copying
- merge: Merge two sorted lists into one
- reverse: Reverse the order of elements in-place
- unique: Remove consecutive duplicate elements
- remove/remove_if: Remove elements matching value/predicate
- sort: Sort elements (merge sort recommended)
// List operations
void splice(const_iterator pos, LinkedList& other);
void splice(const_iterator pos, LinkedList& other, const_iterator it);
void splice(const_iterator pos, LinkedList& other, const_iterator first, const_iterator last);
void merge(LinkedList& other);
template<typename Compare>
void merge(LinkedList& other, Compare comp);
void reverse() noexcept;
void unique();
template<typename BinaryPredicate>
void unique(BinaryPredicate p);
void remove(const T& value);
template<typename UnaryPredicate>
void remove_if(UnaryPredicate p);
void sort();
template<typename Compare>
void sort(Compare comp);
Testing and Benchmarking
Unit Tests (using Google Test or Catch2):
- Test all constructors and assignment operators
- Test all modifiers with various data types
- Test iterator operations and STL algorithm compatibility
- Test exception safety (strong guarantee for modifiers)
- Test memory correctness with valgrind/AddressSanitizer
Performance Benchmarks:
- Benchmark push/pop operations against std::list
- Benchmark traversal with various element counts
- Benchmark splice/merge operations
- Measure memory usage per element
STL Compatibility
Your linked list must be fully compatible with the C++ Standard Template Library. This means working seamlessly with STL algorithms, type traits, and idiomatic C++ patterns.
Your list must work with these STL algorithms:
std::find,std::find_if- element searchstd::count,std::count_if- element countingstd::copy,std::move- element transferstd::for_each- apply function to each elementstd::transform- transform elementsstd::accumulate- reduce operationsstd::distance- iterator distancestd::advance- iterator advancement
Implement proper type aliases and traits:
value_type- element typeallocator_type- allocator typesize_type- unsigned size typedifference_type- signed difference typereference,const_referencepointer,const_pointeriterator,const_iteratorreverse_iterator,const_reverse_iterator
Usage Examples
// Range-based for loop
LinkedList<int> list = {1, 2, 3, 4, 5};
for (const auto& item : list) {
std::cout << item << " ";
}
// STL algorithm usage
auto it = std::find(list.begin(), list.end(), 3);
if (it != list.end()) {
list.erase(it);
}
// Transform with lambda
LinkedList<int> result;
std::transform(list.begin(), list.end(),
std::back_inserter(result),
[](int x) { return x * 2; });
// Initializer list construction
LinkedList<std::string> names = {"Alice", "Bob", "Carol"};
// Move semantics
LinkedList<int> source = {1, 2, 3};
LinkedList<int> dest = std::move(source);
// source is now empty, dest has elements
// Custom allocator
using PoolAlloc = MemoryPoolAllocator<int>;
LinkedList<int, PoolAlloc> pooled_list;
// Emplace for efficiency
struct Person { std::string name; int age; };
LinkedList<Person> people;
people.emplace_back("Alice", 30);
LinkedList<int> list = {3, 1, 4, 1, 5, 9, 2, 6};
list.sort();
list.unique();
auto sum = std::accumulate(list.begin(), list.end(), 0);
std::cout << "Sum of unique sorted elements: " << sum << std::endl;
Performance Optimization
Implement these optimization techniques to achieve performance that matches or exceeds the standard library implementation. Focus on memory efficiency and cache performance.
Memory Pool Allocator
Reduce allocation overhead and fragmentation
Implement a custom memory pool allocator that pre-allocates blocks of nodes:
template<typename T, size_t BlockSize = 4096>
class MemoryPoolAllocator {
public:
using value_type = T;
using pointer = T*;
using size_type = std::size_t;
MemoryPoolAllocator() : current_block_(nullptr), current_slot_(nullptr),
last_slot_(nullptr), free_slots_(nullptr) {}
~MemoryPoolAllocator() {
// Free all allocated blocks
slot_pointer_ curr = current_block_;
while (curr != nullptr) {
slot_pointer_ prev = curr->next;
operator delete(reinterpret_cast<void*>(curr));
curr = prev;
}
}
pointer allocate(size_type n = 1) {
if (free_slots_ != nullptr) {
pointer result = reinterpret_cast<pointer>(free_slots_);
free_slots_ = free_slots_->next;
return result;
}
if (current_slot_ >= last_slot_) {
allocate_block();
}
return reinterpret_cast<pointer>(current_slot_++);
}
void deallocate(pointer p, size_type n = 1) {
if (p != nullptr) {
reinterpret_cast<slot_*>(p)->next = free_slots_;
free_slots_ = reinterpret_cast<slot_*>(p);
}
}
private:
union slot_ {
value_type element;
slot_* next;
};
using data_pointer_ = char*;
using slot_pointer_ = slot_*;
slot_pointer_ current_block_;
slot_pointer_ current_slot_;
slot_pointer_ last_slot_;
slot_pointer_ free_slots_;
size_type padPointer(data_pointer_ p, size_type align) const {
uintptr_t result = reinterpret_cast<uintptr_t>(p);
return ((align - result) % align);
}
void allocate_block() {
data_pointer_ new_block = reinterpret_cast<data_pointer_>(
operator new(BlockSize));
reinterpret_cast<slot_pointer_>(new_block)->next = current_block_;
current_block_ = reinterpret_cast<slot_pointer_>(new_block);
data_pointer_ body = new_block + sizeof(slot_pointer_);
size_type body_padding = padPointer(body, alignof(slot_));
current_slot_ = reinterpret_cast<slot_pointer_>(body + body_padding);
last_slot_ = reinterpret_cast<slot_pointer_>(
new_block + BlockSize - sizeof(slot_) + 1);
}
};
Benefits
- O(1) allocation and deallocation
- Reduced memory fragmentation
- Better cache locality within blocks
- Fewer system calls (malloc/free)
Considerations
- Higher initial memory usage
- Block size tuning needed
- Not suitable for variable-size objects
- Thread safety requires locking
Cache-Aware Design
Improve cache hit rates during traversal
Linked lists are inherently cache-unfriendly due to pointer chasing. Implement these techniques:
Unrolled Linked List
Store multiple elements per node to improve cache utilization:
template<typename T, size_t NodeCapacity = 16>
struct UnrolledNode {
std::array<T, NodeCapacity> elements;
size_t count;
UnrolledNode* prev;
UnrolledNode* next;
// Elements stored contiguously
// Better cache performance on traversal
};
Prefetching
Use compiler hints to prefetch next nodes:
void traverse() {
Node* curr = head_;
while (curr != nullptr) {
// Prefetch next node while processing current
if (curr->next != nullptr) {
__builtin_prefetch(curr->next, 0, 1);
}
process(curr->data);
curr = curr->next;
}
}
Move Semantics & Perfect Forwarding
Eliminate unnecessary copies
Implement efficient move operations and perfect forwarding for element construction:
// Move constructor - O(1), no element copies
LinkedList(LinkedList&& other) noexcept
: head_(other.head_), tail_(other.tail_), size_(other.size_),
allocator_(std::move(other.allocator_)) {
other.head_ = nullptr;
other.tail_ = nullptr;
other.size_ = 0;
}
// Move assignment operator
LinkedList& operator=(LinkedList&& other) noexcept {
if (this != &other) {
clear(); // Free existing nodes
head_ = other.head_;
tail_ = other.tail_;
size_ = other.size_;
allocator_ = std::move(other.allocator_);
other.head_ = nullptr;
other.tail_ = nullptr;
other.size_ = 0;
}
return *this;
}
// Perfect forwarding with emplace
template<typename... Args>
reference emplace_back(Args&&... args) {
Node* new_node = create_node(std::forward<Args>(args)...);
if (tail_ == nullptr) {
head_ = tail_ = new_node;
} else {
tail_->next = new_node;
new_node->prev = tail_;
tail_ = new_node;
}
++size_;
return new_node->data;
}
// Efficient splice - O(1), no copies
void splice(const_iterator pos, LinkedList& other) {
if (other.empty()) return;
Node* before = pos.node_->prev;
Node* after = pos.node_;
// Link other's head
if (before != nullptr) {
before->next = other.head_;
} else {
head_ = other.head_;
}
other.head_->prev = before;
// Link other's tail
other.tail_->next = after;
if (after != nullptr) {
after->prev = other.tail_;
} else {
tail_ = other.tail_;
}
size_ += other.size_;
other.head_ = other.tail_ = nullptr;
other.size_ = 0;
}
Performance Benchmarking
Measure and compare performance metrics
Use Google Benchmark or a custom benchmarking harness to measure performance:
#include <benchmark/benchmark.h>
#include "linked_list.hpp"
#include <list>
// Benchmark push_back operation
static void BM_CustomList_PushBack(benchmark::State& state) {
for (auto _ : state) {
LinkedList<int> list;
for (int i = 0; i < state.range(0); ++i) {
list.push_back(i);
}
benchmark::DoNotOptimize(list);
}
state.SetComplexityN(state.range(0));
}
static void BM_StdList_PushBack(benchmark::State& state) {
for (auto _ : state) {
std::list<int> list;
for (int i = 0; i < state.range(0); ++i) {
list.push_back(i);
}
benchmark::DoNotOptimize(list);
}
state.SetComplexityN(state.range(0));
}
// Benchmark traversal
static void BM_CustomList_Traverse(benchmark::State& state) {
LinkedList<int> list;
for (int i = 0; i < state.range(0); ++i) {
list.push_back(i);
}
for (auto _ : state) {
int sum = 0;
for (const auto& item : list) {
sum += item;
}
benchmark::DoNotOptimize(sum);
}
state.SetComplexityN(state.range(0));
}
BENCHMARK(BM_CustomList_PushBack)->Range(100, 1000000)->Complexity();
BENCHMARK(BM_StdList_PushBack)->Range(100, 1000000)->Complexity();
BENCHMARK(BM_CustomList_Traverse)->Range(100, 1000000)->Complexity();
BENCHMARK_MAIN();
Expected Benchmark Results
| Operation | Elements | std::list (μs) | Your List (μs) | Target Improvement |
|---|---|---|---|---|
| push_back (each) | 10,000 | 0.18 | 0.12 | 33% faster |
| push_front (each) | 10,000 | 0.19 | 0.12 | 37% faster |
| full traversal | 100,000 | 1,340 | 1,100 | 18% faster |
| splice (full list) | 10,000 | 0.05 | 0.05 | match |
| sort | 10,000 | 2,500 | 2,200 | 12% faster |
Performance Optimization Tips
Memory Layout
- Use memory pools for nodes
- Align nodes to cache lines
- Minimize node overhead
- Consider unrolled lists
Code Efficiency
- Use move semantics everywhere
- Perfect forwarding for emplace
- Inline small functions
- Avoid virtual functions
Profiling Tools
- perf for CPU profiling
- valgrind --tool=cachegrind
- Google Benchmark
- AddressSanitizer
Code Structure
Organize your project with a clean, modular structure. Use header-only templates for the core library and separate compilation units for tests and benchmarks.
cpp-linked-list/
├── include/
│ ├── linked_list.hpp # Doubly linked list implementation
│ ├── forward_list.hpp # Singly linked list implementation
│ ├── iterator.hpp # Iterator implementations
│ ├── allocator/
│ │ ├── pool_allocator.hpp # Memory pool allocator
│ │ └── arena_allocator.hpp # Arena allocator (bonus)
│ └── detail/
│ ├── node.hpp # Node structures
│ └── traits.hpp # Type traits helpers
├── src/
│ └── benchmark_main.cpp # Benchmark entry point
├── tests/
│ ├── test_linked_list.cpp # Doubly linked list tests
│ ├── test_forward_list.cpp # Singly linked list tests
│ ├── test_iterator.cpp # Iterator tests
│ ├── test_allocator.cpp # Allocator tests
│ └── test_stl_compat.cpp # STL algorithm compatibility tests
├── benchmarks/
│ ├── bench_operations.cpp # Operation benchmarks
│ ├── bench_memory.cpp # Memory usage benchmarks
│ └── bench_comparison.cpp # Comparison with std::list
├── data/
│ └── (downloaded CSV files) # Benchmark data files
├── docs/
│ ├── API.md # API documentation
│ ├── BENCHMARKS.md # Benchmark results
│ └── DESIGN.md # Design decisions
├── CMakeLists.txt # CMake build configuration
├── README.md # Project documentation
└── .gitignore
Class Hierarchy
Core Classes
LinkedList<T, Allocator>
├── Node (internal)
├── iterator
├── const_iterator
├── reverse_iterator
└── const_reverse_iterator
ForwardList<T, Allocator>
├── Node (internal)
├── iterator
└── const_iterator
Allocator Classes
std::allocator<T> (default)
MemoryPoolAllocator<T, BlockSize>
├── allocate(n)
├── deallocate(p, n)
├── construct(p, args...)
└── destroy(p)
ArenaAllocator<T> (bonus)
├── allocate(n)
├── deallocate(p, n) // no-op
└── reset()
Submission Requirements
Create a public GitHub repository with the exact name shown below:
Required Repository Name
cpp-linked-list
Required Project Structure
cpp-linked-list/
├── include/
│ ├── linked_list.hpp # Main doubly linked list header
│ ├── forward_list.hpp # Singly linked list header
│ └── pool_allocator.hpp # Memory pool allocator
├── tests/
│ ├── test_linked_list.cpp # Unit tests
│ └── test_stl_compat.cpp # STL compatibility tests
├── benchmarks/
│ └── benchmark.cpp # Performance benchmarks
├── data/
│ └── benchmark_results.csv # Your benchmark results
├── docs/
│ └── BENCHMARKS.md # Benchmark analysis
├── CMakeLists.txt # Build configuration
├── README.md # Project documentation
└── screenshots/
├── benchmark_chart.png # Performance comparison chart
└── memory_usage.png # Memory usage visualization
README.md Required Sections
1. Project Header
- Project title and description
- Your full name and submission date
- Course and project number
2. Features
- List all implemented features
- STL compatibility status
- Optimization techniques used
3. Build Instructions
- Prerequisites (CMake, compiler)
- Build commands
- Running tests and benchmarks
4. API Documentation
- Constructor signatures
- Method descriptions
- Usage examples
5. Benchmark Results
- Performance comparison tables
- Charts/graphs embedded
- Analysis of results
6. Design Decisions
- Why certain optimizations
- Trade-offs made
- Alternative approaches considered
7. Testing
- Test coverage summary
- How to run tests
- Known limitations
8. References
- C++ reference links
- Papers/articles referenced
- Tools used
Do Include
- Header-only library in include/
- Comprehensive unit tests
- Performance benchmarks with results
- CMakeLists.txt for easy building
- Memory pool allocator implementation
- Benchmark charts in screenshots/
Do Not Include
- Build artifacts (build/, cmake-build-*/)
- IDE-specific files (.idea/, .vscode/)
- Object files (*.o, *.obj)
- Executable binaries
- Large data files (use .gitignore)
-Wall -Wextra -Wpedantic
and all tests pass. Run with AddressSanitizer to check for memory errors.
Enter your GitHub username - we will verify your repository automatically
Grading Rubric
Your project will be graded on the following criteria. Total: 500 points.
| Criteria | Points | Description |
|---|---|---|
| Core Implementation | 125 | Singly and doubly linked list with all basic operations working correctly |
| Iterator Support | 75 | Forward, bidirectional, and const iterators with proper traits |
| STL Compatibility | 75 | Works with STL algorithms, range-based for, type traits |
| Performance Optimization | 100 | Memory pool allocator, move semantics, cache-aware design |
| Testing | 50 | Comprehensive unit tests with 90%+ coverage |
| Benchmarking | 50 | Performance comparison with std::list, documented results |
| Documentation | 25 | README quality, code comments, API docs |
| Total | 500 |
Grading Levels
Excellent
Exceeds all requirements with exceptional quality
Good
Meets all requirements with good quality
Satisfactory
Meets minimum requirements
Needs Work
Missing key requirements
Ready to Submit?
Make sure you have completed all requirements and reviewed the grading rubric above.
Submit Your ProjectPre-Submission Checklist
Use this checklist to verify you have completed all requirements before submitting your project.
Core Implementation
Iterator Support
STL Compatibility
Performance
g++ -std=c++17 -Wall -Wextra -Wpedantic and run with valgrind or
AddressSanitizer to ensure no memory leaks or undefined behavior.